diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54faba8a05..557e58931f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,6 +98,7 @@ jobs: - name: Set up C++ compiler run: | if [[ "${{ runner.os }}" == "Linux" ]]; then + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install -y ninja-build sudo apt-get install -y g++-15 || sudo apt-get install -y g++ @@ -131,7 +132,7 @@ jobs: run: pip install . - name: Run simulation examples (a2a3sim) - run: python ci.py -p a2a3sim -c 882c4db -t 600 --clone-protocol https + run: python ci.py -p a2a3sim -c 8830244b -t 600 --clone-protocol https st-sim-a5: runs-on: ${{ matrix.os }} @@ -148,6 +149,7 @@ jobs: - name: Set up C++ compiler run: | if [[ "${{ runner.os }}" == "Linux" ]]; then + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install -y ninja-build sudo apt-get install -y g++-15 || sudo apt-get install -y g++ @@ -181,7 +183,7 @@ jobs: run: pip install . - name: Run simulation examples (a5sim) - run: python ci.py -p a5sim -c 882c4db -t 600 --clone-protocol https + run: python ci.py -p a5sim -c 8830244b -t 600 --clone-protocol https # ---------- Python unit tests (a2a3 hardware) ---------- ut-py-a2a3: @@ -215,7 +217,7 @@ jobs: - name: Run on-device examples (a2a3) run: | export PATH="$HOME/.local/bin:$PATH" - source ${ASCEND_HOME_PATH}/bin/setenv.bash && python ci.py -p a2a3 -d ${DEVICE_RANGE} -c 882c4db -t 600 --clone-protocol https + source ${ASCEND_HOME_PATH}/bin/setenv.bash && python ci.py -p a2a3 -d ${DEVICE_RANGE} -c 8830244b -t 600 --clone-protocol https # ---------- Detect A5 changes (runs on GitHub server, not A5 machine) ---------- @@ -283,11 +285,11 @@ jobs: - name: Build nanobind extension run: | source ${ASCEND_HOME_PATH}/bin/setenv.bash - PTO_ARCH=a5 pip install . + pip install . - name: Run on-device examples (a5) run: | export PATH="$HOME/.local/bin:$PATH" source ${ASCEND_HOME_PATH}/bin/setenv.bash DEVICE_LIST=$(python -c "s,e='${DEVICE_RANGE}'.split('-'); print(','.join(str(i) for i in range(int(s),int(e)+1)))") - task-submit --timeout 1800 --max-time 1800 --device "$DEVICE_LIST" --run "python ci.py -p a5 -d ${DEVICE_RANGE} -c 882c4db -t 1200 --clone-protocol https" + task-submit --timeout 1800 --max-time 1800 --device "$DEVICE_LIST" --run "python ci.py -p a5 -d ${DEVICE_RANGE} -c 8830244b -t 1200 --clone-protocol https" diff --git a/ci.py b/ci.py index 1408451e91..af26a84c10 100644 --- a/ci.py +++ b/ci.py @@ -14,8 +14,9 @@ per device, reusing ChipWorker across tasks that share the same runtime. Usage: - python tools/ci.py -p a2a3 -d 5-8 -c 6622890 -t 600 - python tools/ci.py -p a2a3sim -r tensormap_and_ringbuffer -c 6622890 -t 600 + python ci.py # all sim platforms + python ci.py -p a2a3sim -r tensormap_and_ringbuffer -c 6622890 # single platform + python ci.py -p a2a3 -d 5-8 -c 6622890 -t 600 # hardware with devices """ from __future__ import annotations @@ -1100,7 +1101,7 @@ def _discover_valid_platforms() -> list[str]: def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Batch CI test runner with ChipWorker reuse") - parser.add_argument("-p", "--platform", required=True) + parser.add_argument("-p", "--platform", default=None) parser.add_argument("-d", "--device", dest="device_range", default="0") parser.add_argument("-r", "--runtime", default=None) parser.add_argument( @@ -1149,44 +1150,34 @@ def _watchdog_handler(signum, frame): signal.signal(signal.SIGALRM, previous_handler) -def main() -> int: - logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s", force=True) - - args = parse_args() - args.devices = parse_device_range(args.device_range) +def _run_single_platform(platform: str, args: argparse.Namespace) -> list[TaskResult]: + """Run all tasks for a single platform. Returns list of TaskResults.""" + is_sim = platform.endswith("sim") - valid_platforms = _discover_valid_platforms() - if valid_platforms and args.platform not in valid_platforms: - print(f"Unknown platform: {args.platform}") - print(f"Valid platforms: {' '.join(valid_platforms)}") - return 1 - - is_sim = args.platform.endswith("sim") - - # Device-worker sub-command - if args.device_worker: - return device_worker_main(args) - - # Step 1: Discover tasks - tasks = discover_tasks(args.platform, runtime_filter=args.runtime) + tasks = discover_tasks(platform, runtime_filter=args.runtime) if not tasks: - logger.info("No tasks found") - return 0 - logger.info(f"Discovered {len(tasks)} tasks") + logger.info(f"[{platform}] No tasks found") + return [] + logger.info(f"[{platform}] Discovered {len(tasks)} tasks") - # Step 2: Compile and run. + # Compile and run. # Both sim and hw use subprocess isolation (different runtimes cannot share a process). # Within each subprocess, tasks with the same runtime share a ChipWorker. + # Override platform in args for subprocess spawning. + sub_args = argparse.Namespace(**vars(args)) + sub_args.platform = platform if is_sim: - all_results = _run_with_timeout("initial pass", args.timeout, lambda: run_sim_tasks_subprocess(tasks, args)) + all_results = _run_with_timeout( + f"{platform} initial pass", args.timeout, lambda: run_sim_tasks_subprocess(tasks, sub_args) + ) else: all_results = _run_with_timeout( - "initial pass", + f"{platform} initial pass", args.timeout, - lambda: run_hw_tasks_subprocess(tasks, args.devices, args), + lambda: run_hw_tasks_subprocess(tasks, args.devices, sub_args), ) - # Step 3: Pin retry — re-run failed tasks with pinned PTO-ISA commit. + # Pin retry — re-run failed tasks with pinned PTO-ISA commit. final: dict[str, TaskResult] = {} for r in all_results: final[r.name] = r @@ -1195,27 +1186,67 @@ def main() -> int: if failures and args.pto_isa_commit: failed_names = {r.name for r in failures} failed_tasks = [t for t in tasks if t.name in failed_names] - logger.info(f"[CI] {len(failed_tasks)} failure(s), retrying with pinned PTO-ISA {args.pto_isa_commit}") + logger.info(f"[{platform}] {len(failed_tasks)} failure(s), retrying with pinned PTO-ISA {args.pto_isa_commit}") if is_sim: pin_results = _run_with_timeout( - "pin retry", + f"{platform} pin retry", args.timeout, - lambda: run_sim_tasks_subprocess(failed_tasks, args, pto_isa_commit=args.pto_isa_commit), + lambda: run_sim_tasks_subprocess(failed_tasks, sub_args, pto_isa_commit=args.pto_isa_commit), ) else: pin_results = _run_with_timeout( - "pin retry", + f"{platform} pin retry", args.timeout, lambda: run_hw_tasks_subprocess( failed_tasks, args.devices, - args, + sub_args, pto_isa_commit=args.pto_isa_commit, ), ) all_results.extend(pin_results) - # Step 4: Summary + return all_results + + +def main() -> int: + logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s", force=True) + + args = parse_args() + args.devices = parse_device_range(args.device_range) + + valid_platforms = _discover_valid_platforms() + + # Device-worker sub-command (always needs explicit -p) + if args.device_worker: + if not args.platform: + print("--device-worker requires -p/--platform") + return 1 + return device_worker_main(args) + + # Determine which platforms to run + if args.platform: + if args.platform not in valid_platforms: + print(f"Unknown platform: {args.platform}") + print(f"Valid platforms: {' '.join(valid_platforms)}") + return 1 + platforms = [args.platform] + else: + # No -p: run all sim platforms + platforms = [p for p in valid_platforms if p.endswith("sim")] + if not platforms: + print("No sim platforms found") + return 1 + logger.info(f"No platform specified, running all sim platforms: {', '.join(platforms)}") + + all_results: list[TaskResult] = [] + for platform in platforms: + all_results.extend(_run_single_platform(platform, args)) + + if not all_results: + logger.info("No tasks found") + return 0 + return print_summary(all_results) diff --git a/docs/ci.md b/docs/ci.md index 5e5af3d474..1f3f35fcc8 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -8,14 +8,14 @@ Design principles: 1. **Separate jobs per test category** — st, ut-py, and ut-cpp run as independent jobs for parallelism and clear dashboard visibility. 2. **Runner matches hardware tier** — no-hardware tests run on `ubuntu-latest`; platform-specific tests run on self-hosted runners with the matching label (`a2a3`, `a5`). -3. **sim vs onboard for st** — scene tests split into sim jobs (github-hosted, `ci.sh -p a2a3sim`) and onboard jobs (self-hosted, `ci.sh -p a2a3`). +3. **sim vs onboard for st** — scene tests split into sim jobs (github-hosted, `ci.py -p a2a3sim`) and onboard jobs (self-hosted, `ci.py -p a2a3`). ## Full Job Matrix The complete test-type × hardware-tier matrix. Empty cells have no tests yet; only non-empty jobs exist in `ci.yml`. -| | github-hosted | a2a3 runner | a5 runner | -|---|---|---|---| +| Category | github-hosted | a2a3 runner | a5 runner | +| -------- | ------------- | ----------- | --------- | | **ut-py** | `ut-py` | `ut-py-a2a3` | `ut-py-a5` | | **ut-cpp** | *(empty)* | *(empty)* | *(empty)* | | **st** | `st-sim-a2a3`, `st-sim-a5` | `st-onboard-a2a3` | `st-onboard-a5` | @@ -24,7 +24,7 @@ The complete test-type × hardware-tier matrix. Empty cells have no tests yet; o Currently active jobs (a5 jobs commented out — no runner yet): -``` +```text PullRequest ├── ut-py (ubuntu-latest) ├── st-sim-a2a3 (ubuntu + macOS) @@ -36,14 +36,14 @@ PullRequest ``` | Job | Runner | What it runs | -|-----|--------|-------------| +| --- | ------ | ------------ | | `ut-py` | `ubuntu-latest` | `pytest tests -m "not requires_hardware" -v` | -| `st-sim-a2a3` | `ubuntu-latest`, `macos-latest` | `ci.sh -p a2a3sim` | -| `st-sim-a5` | `ubuntu-latest`, `macos-latest` | `ci.sh -p a5sim` | +| `st-sim-a2a3` | `ubuntu-latest`, `macos-latest` | `ci.py -p a2a3sim` | +| `st-sim-a5` | `ubuntu-latest`, `macos-latest` | `ci.py -p a5sim` | | `ut-py-a2a3` | a2a3 self-hosted | `pytest tests -m requires_hardware --platform a2a3 -v` | -| `st-onboard-a2a3` | a2a3 self-hosted | `ci.sh -p a2a3 -d ... --parallel` | +| `st-onboard-a2a3` | a2a3 self-hosted | `ci.py -p a2a3 -d ...` | | `ut-py-a5` | a5 self-hosted | `pytest tests -m requires_hardware --platform a5 -v` | -| `st-onboard-a5` | a5 self-hosted | `ci.sh -p a5 -d ... --parallel` | +| `st-onboard-a5` | a5 self-hosted | `ci.py -p a5 -d ...` | ### Scheduling constraints @@ -56,7 +56,7 @@ PullRequest Three hardware tiers, applied to all test categories. See [testing.md](testing.md#hardware-classification) for the full table including per-category mechanisms (pytest markers, ctest labels, folder structure). | Tier | CI Runner | Job examples | -|------|-----------|-------------| +| ---- | --------- | ------------ | | No hardware | `ubuntu-latest` | `ut-py`, `st-sim-*` | | Platform-specific (a2a3) | `[self-hosted, a2a3]` | `ut-py-a2a3`, `st-onboard-a2a3` | | Platform-specific (a5) | `[self-hosted, a5]` | `ut-py-a5`, `st-onboard-a5` | @@ -68,24 +68,24 @@ Three hardware tiers, applied to all test categories. See [testing.md](testing.m Python unit tests. Run via pytest with marker filtering. | File | Content | Hardware? | -|------|---------|-----------| +| ---- | ------- | --------- | | `test_task_interface.py` | nanobind extension API tests | No | | `test_runtime_builder.py` (mocked classes) | RuntimeBuilder discovery, error handling, build logic | No | | `test_runtime_builder.py::TestRuntimeBuilderIntegration` | Real compilation across platform × runtime | Yes (`@pytest.mark.requires_hardware`) | ### `examples/` — Small examples (sim + onboard) -Small, fast examples that run on both simulation and real hardware. Organized as `examples/{arch}/{runtime}/{name}/`. Discovered and executed by `ci.sh`. +Small, fast examples that run on both simulation and real hardware. Organized as `examples/{arch}/{runtime}/{name}/`. Discovered and executed by `ci.py`. ### `tests/st/` — Scene tests (onboard only) -Large-scale, feature-rich hardware tests. Too slow or using instructions unsupported by the simulator. Organized as `tests/st/{arch}/{runtime}/{name}/`. Discovered and executed by `ci.sh` only when the platform is not sim. +Large-scale, feature-rich hardware tests. Too slow or using instructions unsupported by the simulator. Organized as `tests/st/{arch}/{runtime}/{name}/`. Discovered and executed by `ci.py` only when the platform is not sim. ### Shared structure Both `examples/` and `tests/st/` cases follow the same layout: -``` +```text {name}/ golden.py # generate_inputs() + compute_golden() kernels/ @@ -143,38 +143,39 @@ python tools/test_catalog.py cases --platform a2a3sim --source example python tools/test_catalog.py cases --platform a2a3 --source st --format json ``` -## `ci.sh` — Scene Test Runner +## `ci.py` — Scene Test Runner -`ci.sh` handles scene test execution (examples + st). It does **not** run pytest tests — those are invoked directly by the CI workflow. +`ci.py` handles scene test execution (examples + st). It does **not** run pytest tests — those are invoked directly by the CI workflow. ### Key features -- **Parallel execution**: `--parallel` runs sim tasks concurrently; hardware tasks use a shared device queue with `flock`-based locking. -- **Device queue**: Hardware tasks are distributed across devices specified by `-d`. Workers pop tasks from a shared queue atomically. -- **Retry**: Failed tasks are retried up to 3 times. Hardware workers quarantine a device after exhausting retries. -- **PTO-ISA pinning**: `-c ` pins the PTO-ISA dependency. On first failure, ci.sh cleans the cached clone and retries with the pinned commit. +- **ChipWorker reuse**: Tasks sharing the same runtime reuse a single ChipWorker within their subprocess, avoiding repeated device init/teardown. +- **Subprocess isolation**: Different runtimes run in separate subprocesses (the host `.so` cannot be unloaded within a single process). +- **Device queue**: Hardware tasks are distributed across devices specified by `-d`. Workers pop tasks from a shared queue via threads. +- **Retry**: Failed tasks are retried up to 3 times. Hardware workers quarantine a device after a failure. +- **PTO-ISA pinning**: `-c ` pins the PTO-ISA dependency. On first failure, re-runs failed tasks with the pinned commit. - **Watchdog**: `-t ` sets a timeout. The entire run is aborted if it exceeds the limit. - **Summary table**: After all tasks complete, a formatted results table is printed with pass/fail status, timing, device, and attempt count. ### Usage ```bash -# Simulation (github-hosted) -./ci.sh -p a2a3sim -t 600 +# All sim platforms (no -p: auto-discovers a2a3sim, a5sim, etc.) +python ci.py -t 600 + +# Single sim platform +python ci.py -p a2a3sim -c 6622890 -t 600 # Hardware with device range -./ci.sh -p a2a3 -d 4-7 --parallel -t 600 +python ci.py -p a2a3 -d 4-7 -c 6622890 -t 600 # Filter by runtime -./ci.sh -p a2a3sim -r tensormap_and_ringbuffer - -# Pin PTO-ISA commit -./ci.sh -p a2a3sim -c 6622890 +python ci.py -p a2a3sim -r tensormap_and_ringbuffer ``` ### Task discovery -`ci.sh` scans two directories: +`ci.py` scans two directories: 1. `examples/` — included for both sim and onboard platforms. 2. `tests/st/` — included only for onboard platforms (non-sim). @@ -183,14 +184,13 @@ For each directory, it walks subdirectories looking for `kernels/kernel_config.p ### Execution flow -``` -1. Parse arguments (-p, -d, --parallel, -r, -c, -t) -2. Discover platforms and runtimes from src/ -3. Discover tasks from examples/ and tests/st/ -4. Run sim tasks (parallel or sequential) - └── On failure + -c flag: pin PTO-ISA, retry -5. Run hardware tasks (device queue with workers) - └── On failure + -c flag: pin PTO-ISA, retry -6. Print summary table -7. Exit 0 if all passed, 1 otherwise +```text +1. Parse arguments (-p, -d, -r, -c, -t) +2. If no -p: auto-discover all sim platforms and run each +3. For each platform: + a. Discover tasks from examples/ and tests/st/ + b. Run tasks (subprocess per runtime group for sim, device queue for hw) + └── On failure + -c flag: pin PTO-ISA, retry failed tasks +4. Print combined summary table +5. Exit 0 if all passed, 1 otherwise ``` diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 67d4bd2175..64255dfab7 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -80,6 +80,11 @@ Runtime binaries (host `.so`, aicpu `.so`, aicore `.o`) are pre-built during `pi Persistent cmake build directories under `build/cache/` enable incremental compilation — only changed files are recompiled. +**Architecture note:** a2a3 and a5 differ only at runtime (device selection, block dimensions, etc.). The compiled binaries are architecture-independent — the same toolchain and flags produce artifacts that work on both chips. Therefore `pip install .` should build **all** architectures (both a2a3 and a5, both onboard and sim) whenever the corresponding toolchain is available. Toolchain detection (`build_runtimes.py`): + +- **sim** (a2a3sim, a5sim): requires `gcc` + `g++` in `PATH` +- **onboard** (a2a3, a5): requires `ccec` in `PATH` + cross-compiler under `ASCEND_HOME_PATH` + ### User code (per-example) 1. `python/kernel_compiler.py` — compiles user-written kernel `.cpp` files (one per `func_id`) @@ -123,7 +128,7 @@ Run with: `python examples/scripts/run_example.py -k -g (16, 16) // -// pij is float16 (converted from fp32 in softmax_prepare via TCVT). +// pij is bfloat16 (converted from fp32 in softmax_prepare via TCVT). // vj is stored as (K, N) = (block_size, head_dim) in row-major (ND) layout. // Standard non-transposed B pattern: ND GlobalB + ColMajor/RowMajor TileMatB. @@ -33,13 +33,13 @@ using namespace pto; template static __aicore__ void pv_matmul_impl(__gm__ Tensor *pij, __gm__ Tensor *vj, __gm__ Tensor *oi) { - __gm__ half *pij_addr = reinterpret_cast<__gm__ half *>(pij->buffer.addr); - __gm__ half *vj_addr = reinterpret_cast<__gm__ half *>(vj->buffer.addr); + __gm__ bfloat16_t *pij_addr = reinterpret_cast<__gm__ bfloat16_t *>(pij->buffer.addr); + __gm__ bfloat16_t *vj_addr = reinterpret_cast<__gm__ bfloat16_t *>(vj->buffer.addr); __gm__ float *oi_addr = reinterpret_cast<__gm__ float *>(oi->buffer.addr); - // pij (M, K) fp16, vj (K, N) fp16 in ND (row-major), oi_new (M, N) fp32 - using GlobalA = GlobalTensor, Stride>; - using GlobalB = GlobalTensor, Stride>; + // pij (M, K) bf16, vj (K, N) bf16 in ND (row-major), oi_new (M, N) fp32 + using GlobalA = GlobalTensor, Stride>; + using GlobalB = GlobalTensor, Stride>; using GlobalOut = GlobalTensor, Stride>; GlobalA pijGlobal(pij_addr + pij->start_offset); @@ -47,12 +47,12 @@ static __aicore__ void pv_matmul_impl(__gm__ Tensor *pij, __gm__ Tensor *vj, __g GlobalOut oiGlobal(oi_addr + oi->start_offset); // L1 Mat tiles: standard ND pattern for both A and B - using TileMatA = Tile; - using TileMatB = Tile; + using TileMatA = Tile; + using TileMatB = Tile; // L0 tiles - using LeftTile = TileLeft; - using RightTile = TileRight; + using LeftTile = TileLeft; + using RightTile = TileRight; using AccTile = TileAcc; TileMatA aMatTile; diff --git a/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aic/aic_qk_matmul.cpp b/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aic/aic_qk_matmul.cpp index aac34aef79..2db78900ab 100644 --- a/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aic/aic_qk_matmul.cpp +++ b/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aic/aic_qk_matmul.cpp @@ -33,14 +33,14 @@ using namespace pto; template static __aicore__ void qk_matmul_impl(__gm__ Tensor *qi, __gm__ Tensor *kj, __gm__ Tensor *sij) { - __gm__ half *qi_addr = reinterpret_cast<__gm__ half *>(qi->buffer.addr); - __gm__ half *kj_addr = reinterpret_cast<__gm__ half *>(kj->buffer.addr); + __gm__ bfloat16_t *qi_addr = reinterpret_cast<__gm__ bfloat16_t *>(qi->buffer.addr); + __gm__ bfloat16_t *kj_addr = reinterpret_cast<__gm__ bfloat16_t *>(kj->buffer.addr); __gm__ float *sij_addr = reinterpret_cast<__gm__ float *>(sij->buffer.addr); - // qi (M, K) fp16 in ND (row-major) layout - using GlobalA = GlobalTensor, Stride>; + // qi (M, K) bf16 in ND (row-major) layout + using GlobalA = GlobalTensor, Stride>; // kj stored as (N, K) row-major = (K, N) column-major -> DN layout - using GlobalB = GlobalTensor, Stride, Layout::DN>; + using GlobalB = GlobalTensor, Stride, Layout::DN>; using GlobalOut = GlobalTensor, Stride>; GlobalA qiGlobal(qi_addr + qi->start_offset); @@ -48,12 +48,12 @@ static __aicore__ void qk_matmul_impl(__gm__ Tensor *qi, __gm__ Tensor *kj, __gm GlobalOut sijGlobal(sij_addr + sij->start_offset); // L1 Mat tiles: A is standard ND, B uses transposed-B pattern (RowMajor/ColMajor) - using TileMatA = Tile; - using TileMatB = Tile; + using TileMatA = Tile; + using TileMatB = Tile; // L0 tiles - using LeftTile = TileLeft; - using RightTile = TileRight; + using LeftTile = TileLeft; + using RightTile = TileRight; using AccTile = TileAcc; TileMatA aMatTile; diff --git a/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aiv/aiv_softmax_prepare.cpp b/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aiv/aiv_softmax_prepare.cpp index 9483ddd1d0..fc39bc2ef7 100644 --- a/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aiv/aiv_softmax_prepare.cpp +++ b/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/aiv/aiv_softmax_prepare.cpp @@ -46,18 +46,18 @@ static __aicore__ void softmax_prepare_impl( ) { uint64_t valid_len = static_cast(sij->shapes[1]); __gm__ float *sij_addr = reinterpret_cast<__gm__ float *>(sij->buffer.addr); - __gm__ half *pij_addr = reinterpret_cast<__gm__ half *>(pij->buffer.addr); + __gm__ bfloat16_t *pij_addr = reinterpret_cast<__gm__ bfloat16_t *>(pij->buffer.addr); __gm__ float *mij_addr = reinterpret_cast<__gm__ float *>(mij->buffer.addr); __gm__ float *lij_addr = reinterpret_cast<__gm__ float *>(lij->buffer.addr); constexpr int kAlignedRows = ((M * sizeof(float) + 31) / 32) * (32 / sizeof(float)); using GlobalDataMxN = GlobalTensor, Stride<1, 1, 1, N, 1>>; - using GlobalDataMxN_f16 = GlobalTensor, Stride<1, 1, 1, N, 1>>; + using GlobalDataMxN_bf16 = GlobalTensor, Stride<1, 1, 1, N, 1>>; using GlobalScalarDN = GlobalTensor, Stride<1, 1, 1, 1, 1>, Layout::DN>; GlobalDataMxN sijGlobal(sij_addr + sij->start_offset); - GlobalDataMxN_f16 pijGlobal(pij_addr + pij->start_offset); + GlobalDataMxN_bf16 pijGlobal(pij_addr + pij->start_offset); GlobalScalarDN mijGlobal(mij_addr + mij->start_offset); GlobalScalarDN lijGlobal(lij_addr + lij->start_offset); @@ -67,7 +67,7 @@ static __aicore__ void softmax_prepare_impl( using TileSijPad = Tile; using TileVecMxN = Tile; - using TileVecMxN_f16 = Tile; + using TileVecMxN_bf16 = Tile; using TileScalarDN = Tile; TileVecMxN sijTile; @@ -77,7 +77,7 @@ static __aicore__ void softmax_prepare_impl( TileVecMxN tmpTile; TileScalarDN maxTile; TileScalarDN sumTile; - TileVecMxN_f16 pijF16Tile; + TileVecMxN_bf16 pijBf16Tile; TASSIGN(sijTile, 0x0); TASSIGN(sijDynTile, 0x0); @@ -86,7 +86,7 @@ static __aicore__ void softmax_prepare_impl( TASSIGN(tmpTile, 2 * M * N * sizeof(float)); TASSIGN(maxTile, 3 * M * N * sizeof(float)); TASSIGN(sumTile, 3 * M * N * sizeof(float) + kAlignedRows * sizeof(float)); - TASSIGN(pijF16Tile, 3 * M * N * sizeof(float) + 2 * kAlignedRows * sizeof(float)); + TASSIGN(pijBf16Tile, 3 * M * N * sizeof(float) + 2 * kAlignedRows * sizeof(float)); // Load full sij (M, N) tile from GM - all N columns including garbage for partial blocks TLOAD(sijTile, sijGlobal); @@ -104,16 +104,16 @@ static __aicore__ void softmax_prepare_impl( TROWEXPANDSUB(pijTile, sijTile, maxTile); pipe_barrier(PIPE_V); TEXP(pijTile, pijTile); - // Truncate pij to fp16 first, then compute lij from truncated values (matches golden) - TCVT(pijF16Tile, pijTile, RoundMode::CAST_ROUND); - TCVT(pijTile, pijF16Tile, RoundMode::CAST_ROUND); + // Truncate pij to bf16 first, then compute lij from truncated values (matches golden) + TCVT(pijBf16Tile, pijTile, RoundMode::CAST_ROUND); + TCVT(pijTile, pijBf16Tile, RoundMode::CAST_ROUND); TROWSUM(sumTile, pijTile, tmpTile); set_flag(PIPE_V, PIPE_MTE3, EVENT_ID0); wait_flag(PIPE_V, PIPE_MTE3, EVENT_ID0); TSTORE(mijGlobal, maxTile); TSTORE(lijGlobal, sumTile); - TSTORE(pijGlobal, pijF16Tile); + TSTORE(pijGlobal, pijBf16Tile); set_flag(PIPE_MTE3, PIPE_S, EVENT_ID7); wait_flag(PIPE_MTE3, PIPE_S, EVENT_ID7); diff --git a/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/orchestration/paged_attention_orch.cpp b/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/orchestration/paged_attention_orch.cpp index 76fc90a76d..7494b4274d 100644 --- a/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/orchestration/paged_attention_orch.cpp +++ b/examples/a2a3/tensormap_and_ringbuffer/paged_attention/kernels/orchestration/paged_attention_orch.cpp @@ -15,9 +15,9 @@ * Each block processes a single 16x16 matmul operation. * * Memory Layout: - * Query: (batch, 16, 16) - one 16x16 tile per batch fp16 - * Key: (total_blocks, 16, 16) - stored as K^T for direct matmul fp16 - * Value: (total_blocks, 16, 16) - direct format fp16 + * Query: (batch, 16, 16) - one 16x16 tile per batch bf16 + * Key: (total_blocks, 16, 16) - stored as K^T for direct matmul bf16 + * Value: (total_blocks, 16, 16) - direct format bf16 * * This file compiles as a standalone .so with zero runtime link dependencies. * All runtime calls go through the PTO2RuntimeOps function-pointer table. @@ -106,7 +106,7 @@ __attribute__((visibility("default"))) void aicpu_orchestration_entry(const Chip TensorCreateInfo tile2d_ci(tile2d_shapes, 2, DataType::FLOAT32); TensorCreateInfo scalar_ci(scalar_shapes, 1, DataType::FLOAT32); TensorCreateInfo sij_ci(sij_shapes, 2, DataType::FLOAT32); - TensorCreateInfo pij_f16_ci(sij_shapes, 2, data_type); + TensorCreateInfo pij_bf16_ci(sij_shapes, 2, data_type); for (uint64_t b_idx = 0; b_idx < batch; b_idx++) { uint32_t cl_idx[1] = {static_cast(b_idx)}; @@ -152,17 +152,17 @@ __attribute__((visibility("default"))) void aicpu_orchestration_entry(const Chip Tensor sij_valid = sij.view(sij_valid_shapes, sij_valid_offsets); Arg params_sf; params_sf.add_input(sij_valid); - params_sf.add_output(pij_f16_ci); + params_sf.add_output(pij_bf16_ci); params_sf.add_output(scalar_ci); params_sf.add_output(scalar_ci); params_sf.add_scalar(scale_value); TaskOutputTensors sf_outs = pto2_rt_submit_aiv_task(FUNC_SOFTMAX_PREPARE, params_sf); - const Tensor &pij_f16 = sf_outs.get_ref(0); + const Tensor &pij_bf16 = sf_outs.get_ref(0); const Tensor &mi = sf_outs.get_ref(1); const Tensor &li = sf_outs.get_ref(2); Arg params_pv; - params_pv.add_input(pij_f16); + params_pv.add_input(pij_bf16); params_pv.add_input(vj); params_pv.add_output(tile2d_ci); TaskOutputTensors pv_outs = pto2_rt_submit_aic_task(FUNC_PV_MATMUL, params_pv); diff --git a/examples/a5/tensormap_and_ringbuffer/bgemm/kernels/mix/kernel_bgemm.cpp b/examples/a5/tensormap_and_ringbuffer/bgemm/kernels/mix/kernel_bgemm.cpp index 1a74920e7a..694067335c 100644 --- a/examples/a5/tensormap_and_ringbuffer/bgemm/kernels/mix/kernel_bgemm.cpp +++ b/examples/a5/tensormap_and_ringbuffer/bgemm/kernels/mix/kernel_bgemm.cpp @@ -93,6 +93,7 @@ extern "C" __aicore__ void kernel_entry(__gm__ int64_t *args) { // Pipe and FIFO tile are declared in common scope (both sides reference the type) VecFifoTileT vecFifoTile; + TASSIGN(vecFifoTile, 0x0); PipeT mPipe(nullptr, 0U, 0U); // ========================================================================= diff --git a/examples/scripts/build_runtimes.py b/examples/scripts/build_runtimes.py index d7947ca5bf..4943dabd01 100644 --- a/examples/scripts/build_runtimes.py +++ b/examples/scripts/build_runtimes.py @@ -43,15 +43,18 @@ def detect_buildable_platforms() -> list: """Detect which platforms can be built with available toolchains. Returns: - List of platform strings, e.g. ["a2a3sim", "a5sim"] on x86_64. + List of platform strings, e.g. ["a2a3sim", "a5sim"] when only gcc is available, + or all four when the onboard cross-compiler is also present. """ platforms = [] - # Sim platforms always buildable (only need gcc/g++) + # Sim platforms: only need gcc/g++ if shutil.which("gcc") and shutil.which("g++"): platforms.extend(["a2a3sim", "a5sim"]) - # Onboard platforms need ccec + aarch64 cross-compiler from ASCEND_HOME_PATH + # Onboard platforms: need ccec + cross-compiler from ASCEND_HOME_PATH. + # a2a3 and a5 use the same toolchain and produce identical artifacts; + # the difference is runtime-only, so always build both. has_ccec = shutil.which("ccec") is not None ascend_home = os.environ.get("ASCEND_HOME_PATH", "") @@ -59,28 +62,11 @@ def detect_buildable_platforms() -> list: has_cross = os.path.isfile(cross_gxx) if has_cross and has_ccec: - local_arch = _detect_local_chip_arch() - if local_arch: - platforms.append(local_arch) + platforms.extend(["a2a3", "a5"]) return platforms -def _detect_local_chip_arch() -> str: - """Detect chip architecture on aarch64 hardware. - - Returns "a2a3" or "a5" based on environment or device files. - Falls back to "a2a3" if detection is ambiguous. - """ - # Explicit override - env_arch = os.environ.get("PTO_ARCH") - if env_arch in ("a2a3", "a5"): - return env_arch - - # Default to a2a3 on aarch64 hardware - return "a2a3" - - def build_all( lib_dir: Path, cache_dir: Path,