Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 113 additions & 90 deletions docs/design/a5-fg-pg-core-selection.en.md

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions docs/dfx/args-dump.md
Original file line number Diff line number Diff line change
Expand Up @@ -841,17 +841,16 @@ host hand-off queue).

### 7.5 Configuration knobs

All defaults live in
[`platform_config.h`](../../src/a2a3/platform/include/common/platform_config.h)
and match between `a2a3` and `a5`:
All defaults live in each platform's `platform_config.h`. The values below
match between `a2a3` and `a5` except for the platform thread cap:

| Constant | Default | Effect |
| -------- | ------- | ------ |
| `PLATFORM_DUMP_RECORDS_PER_BUFFER` | 256 | Max records per DumpBuffer (a2a3: per metadata buffer) |
| `PLATFORM_DUMP_BUFFERS_PER_THREAD` | 8 | Arena size multiplier (a2a3: also SPSC free queue depth) |
| `PLATFORM_DUMP_AVG_TENSOR_BYTES` | 64 KiB | Arena size multiplier |
| `PLATFORM_DUMP_MAX_DIMS` | 5 | Upper bound on shape / offset arrays |
| `PLATFORM_MAX_AICPU_THREADS` | 7 | Number of dump-producing threads |
| `PLATFORM_MAX_AICPU_THREADS` | a2a3: 4; a5: 5 | Maximum number of dump-producing threads |

Per-thread arena =
`BUFFERS_PER_THREAD × RECORDS_PER_BUFFER × AVG_TENSOR_BYTES`
Expand Down
4 changes: 2 additions & 2 deletions docs/dfx/chip-swimlane-profiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ a5's `ChipSwimlaneCollector` derives from
`ProfilerBase<ChipSwimlaneCollector, ChipSwimlaneModule>` and uses the same
framework abstractions as a2a3, including the same split mgmt +
collector shard shape (`kMaxCollectorThreads` =
`PLATFORM_MAX_AICPU_THREADS`, i.e. 7 on a5 vs 4 on a2a3, capping the
`PLATFORM_MAX_AICPU_THREADS`, i.e. 5 on a5 vs 4 on a2a3, capping the
shard arrays; the live drain/collector count is
`min(aicpu_thread_num, kMaxCollectorThreads)`). The
behavioral deviation from §5.2 is the **transport channel**: a5 has no
Expand Down Expand Up @@ -956,7 +956,7 @@ PHASE), same shape as a2a3.
| AICPU commit on FIN | identical | |
| Buffer model | rotating pool (free + ready queues) per kind | identical |
| Ready queue | per-AICPU-thread, multiplexes 4 kinds via `ReadyQueueEntry::kind` | per-AICPU-thread, 2 kinds via `is_phase` |
| Host threads | split mgmt + collector shards, streams during execution | same split mgmt + collector shards (7 = `PLATFORM_MAX_AICPU_THREADS` vs a2a3's 4) |
| Host threads | split mgmt + collector shards, streams during execution | same split mgmt + collector shards (5 = `PLATFORM_MAX_AICPU_THREADS` vs a2a3's 4) |
| Host-class shape | `ProfilerBase<ChipSwimlaneCollector, ChipSwimlaneModule>` (`kBufferKinds = 4`) | same base, `kBufferKinds = 2` |
| Host transport | `halHostRegister` shared memory | host-shadow `malloc` + per-tick `rtMemcpy`/`memcpy` |
| `MemoryOps` callbacks | 3 (`alloc`, `reg`, `free_`) | 5 (+ `copy_to_device`, `copy_from_device`) |
Expand Down
4 changes: 2 additions & 2 deletions docs/dfx/pmu-profiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ a2a3). At shutdown, AICPU flushes any partially-filled buffers via
a5's `PmuCollector` derives from
`ProfilerBase<PmuCollector, PmuModule>` and uses the same framework
abstractions as a2a3, including the same split mgmt + collector shard
shape (`kMaxCollectorThreads` = `PLATFORM_MAX_AICPU_THREADS`, i.e. 7 on
shape (`kMaxCollectorThreads` = `PLATFORM_MAX_AICPU_THREADS`, i.e. 5 on
a5 vs 4 on a2a3, capping the shard arrays; the live drain/collector
count is `min(aicpu_thread_num, kMaxCollectorThreads)`). The
behavioral deviation from §5.2 is the **transport channel**: a5 has no
Expand Down Expand Up @@ -502,7 +502,7 @@ device-side counters.
| Counter readout | AICPU MMIO `read_reg` | AICore MMIO `ld_dev` |
| Per-core staging | direct write into `records[count]` | dual-issue slots, AICPU commits on FIN |
| Buffer model | rotating pool (free + ready queues, SPSC protocol) | identical |
| Host threads | split mgmt + collector shards, writes shard-local temp files during execution and merges at reconcile | same split mgmt + collector shards (7 = `PLATFORM_MAX_AICPU_THREADS` vs a2a3's 4) |
| Host threads | split mgmt + collector shards, writes shard-local temp files during execution and merges at reconcile | same split mgmt + collector shards (5 = `PLATFORM_MAX_AICPU_THREADS` vs a2a3's 4) |
| Host-class shape | `ProfilerBase<PmuCollector, PmuModule>` | identical |
| Host transport | `halHostRegister` shared memory | host-shadow `malloc` + per-tick `rtMemcpy`/`memcpy` |
| `MemoryOps` callbacks | 3 (`alloc`, `reg`, `free_`) | 5 (+ `copy_to_device`, `copy_from_device`) |
Expand Down
3 changes: 3 additions & 0 deletions simpler_setup/runtime_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ def _build(actual_build_dir: str) -> Union[bytes, Path]:
od.mkdir(parents=True, exist_ok=True)
dest = od / binary_name
shutil.copy2(binary_path, dest)
if target_platform == "host" and self.platform == "a5":
topo_fallback = Path(cmake_source_dir) / "aicpu_cpu_topo_fallback.json"
shutil.copy2(topo_fallback, od / topo_fallback.name)
return dest
else:
with open(binary_path, "rb") as f:
Expand Down
68 changes: 59 additions & 9 deletions src/a5/docs/hardware.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,48 @@ The same driver returns `DRV_ERROR_NOT_SUPPORT` for both
`dsmi_get_device_info(SOC_INFO, CPU_TOPO)`. Its public DSMI header only
defines SOC_INFO subcommands 0 and 1.

The OCCUPY-only fallback is restricted to the verified x86 standard-card
signature `Ascend950PR_9579` with `OCCUPY=0x3e`. In that case, the host
runtime treats each set bit as a distinct non-SMT physical CPU. Other hosts,
SoCs, and masks remain unsupported when CPU_TOPO is unavailable. Drivers
that provide CPU_TOPO continue to use its detailed physical and hyperthread
metadata.
The packaged JSON preserves verified CPU_TOPO-less signatures, including
logical-to-physical mapping and selection policy. A signature is used only
when its SoC and every constraint declared by that entry match. The 9599 entry
is host-independent and requires exact device-side `OCCUPY=0x1f8`; the 9579
entry additionally constrains host architecture. The verified generic policy
continues to honor an explicit `aicpu_thread_num`.
When neither the driver nor a verified JSON entry provides CPU_TOPO, the
runtime uses the set bits in OCCUPY as schedulable CPU IDs and applies the
unknown-topology fallback without inferring physical cores, SMT siblings,
clusters, or dies.

Live driver topology is still preferred when present. Hardware signatures
without a matching packaged entry use the generic OCCUPY-only fallback and
emit a CPU_TOPO-unavailable warning.

### Live FG CPU_TOPO example (`Ascend950PR_9599`)

The following topology was measured on an FG device with CANN 9.2.0. It is
also the source of the verified 9599 packaged fallback:

```text
cpu_id=0 phy_cpu_id=0 hyperthread_id=0 is_share=0 cpu_mask=0x1
cpu_id=1 phy_cpu_id=1 hyperthread_id=0 is_share=1 cpu_mask=0x6
cpu_id=2 phy_cpu_id=1 hyperthread_id=1 is_share=1 cpu_mask=0x6
cpu_id=3 phy_cpu_id=2 hyperthread_id=0 is_share=0 cpu_mask=0x8
cpu_id=4 phy_cpu_id=3 hyperthread_id=0 is_share=0 cpu_mask=0x10
cpu_id=5 phy_cpu_id=4 hyperthread_id=0 is_share=0 cpu_mask=0x20
cpu_id=6 phy_cpu_id=5 hyperthread_id=0 is_share=0 cpu_mask=0x40
cpu_id=7 phy_cpu_id=6 hyperthread_id=0 is_share=0 cpu_mask=0x80
cpu_id=8 phy_cpu_id=7 hyperthread_id=0 is_share=0 cpu_mask=0x100
```

Each row describes one logical CPU. Equal `phy_cpu_id` values identify SMT
siblings, and `hyperthread_id` distinguishes the two logical threads. A5 maps
two physical CPUs to one cluster and two clusters to one die, so the runtime
derives `cluster_id = phy_cpu_id / 2` and `die_id = phy_cpu_id / 4`.

For this device, `OS_SCHED=0x1` and `OCCUPY=PF_OCCUPY=0x1f8`. Therefore cpu 0
belongs to the AICPU OS, cpu 1/2 form a Data SMT pair outside the user pool,
and cpu 3..8 are the six Compute CPUs that may receive Scheduler or
Orchestrator roles. The four surviving clusters classify the device as FG;
the automatic `1O+4S` policy selects five of those six Compute CPUs.

## CANN AICPU thread dispatch under varying launch budgets

Expand Down Expand Up @@ -228,9 +264,9 @@ Scenario A (OCCUPY=0x1f8, 6 user cpus):
the failure as `aclrtSynchronizeStream rc=507000` (runtime internal)
after the launch.

The runtime implements the safe choice: the host's topology probe sets
`runtime->aicpu_launch_count = popcount(OCCUPY)` after reading the
device-side OCCUPY, and the host's `rtsLaunchCpuKernel` is called with
The runtime implements the safe choice: a one-thread preflight AICPU query
reads device-side OCCUPY, then the host topology probe sets
`runtime->aicpu_launch_count = popcount(OCCUPY)`. The host's `rtsLaunchCpuKernel` is called with
that exact value. `PLATFORM_MAX_AICPU_THREADS_JUST_FOR_LAUNCH = 14`
remains a compile-time **upper bound** (array sizes, headroom), not the
actual launch count. See:
Expand All @@ -243,6 +279,20 @@ actual launch count. See:
- `src/common/platform/onboard/aicpu/platform_aicpu_affinity.cpp` —
`platform_aicpu_affinity_gate_filter()` (the post-hoc classifier)

If CPU_TOPO does not match FG, PG1, or PG2 by **surviving cluster/die
layout** (logical CPU count is not a gate), the runtime uses a deterministic
fallback: valid metadata is sorted by `(die, cluster,
physical CPU, hyperthread, logical CPU)`; OCCUPY-only metadata reduces this to
logical CPU ID order. Automatic mode keeps at most five and may shrink to the
available count. A manual request from 2 through 5 must be satisfied exactly.
The last selection is the Orchestrator and preceding selections are
Schedulers. The launch count remains the full device-side OCCUPY population
required by the filter gate and may therefore exceed the active cap. On
`Ascend950PR_9599`, a measured 9-logical layout with four clusters classifies
as FG. Scheduler SMT availability is recorded separately and does not define
another scenario. When live CPU_TOPO was unavailable, the JSON or OCCUPY-only
source independently triggers the required warning.

The 0x7ffe SKU's dispatch behavior at `aicpu_num=14` has **not yet
been measured** — once an a5 0x7ffe device runs an a5 onboard test,
update this section with the observed (cpu_id → thread) spread. If
Expand Down
28 changes: 28 additions & 0 deletions src/a5/platform/include/common/kernel_args.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

#include <cstddef>
#include <cstdint>
#include <type_traits>

#include "common/dma_workspace.h"

Expand Down Expand Up @@ -137,6 +138,33 @@ struct InitArgs {
uint64_t dma_workspace_addr[DMA_WORKSPACE_KIND_COUNT]{};
};

struct AicpuTopologyQueryResult {
int32_t occupy_rc{-1};
int32_t pf_occupy_rc{-1};
int32_t os_sched_rc{-1};
int32_t reserved{0};
uint64_t occupy{0};
uint64_t pf_occupy{0};
uint64_t os_sched{0};
};

struct AicpuTopologyQueryArgs {
uint64_t result_addr{0};
};

static_assert(
std::is_trivially_copyable_v<AicpuTopologyQueryResult> && std::is_standard_layout_v<AicpuTopologyQueryResult>,
"AicpuTopologyQueryResult must remain a memcpy-safe wire type"
);
static_assert(sizeof(AicpuTopologyQueryResult) == 40, "AicpuTopologyQueryResult ABI size drift");
static_assert(offsetof(AicpuTopologyQueryResult, occupy) == 16, "AicpuTopologyQueryResult::occupy offset drift");
static_assert(offsetof(AicpuTopologyQueryResult, os_sched) == 32, "AicpuTopologyQueryResult::os_sched offset drift");
static_assert(
std::is_trivially_copyable_v<AicpuTopologyQueryArgs> && std::is_standard_layout_v<AicpuTopologyQueryArgs>,
"AicpuTopologyQueryArgs must remain a memcpy-safe wire type"
);
static_assert(sizeof(AicpuTopologyQueryArgs) == 8, "AicpuTopologyQueryArgs ABI size drift");

/**
* RegisterCallableArgs - device orchestration SO registration payload
*
Expand Down
15 changes: 8 additions & 7 deletions src/a5/platform/include/common/platform_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ constexpr int PLATFORM_AIV_CORES_PER_BLOCKDIM = 2;
* Maximum AICPU scheduling threads
* Determines parallelism level of the AICPU task scheduler.
*/
constexpr int PLATFORM_MAX_AICPU_THREADS = 7;
constexpr int PLATFORM_MAX_AICPU_THREADS = 5;

/**
* Default active AICPU thread count when aicpu_thread_num is left at 0 (auto):
* 1 orchestrator + 4 schedulers. Onboard topology probing assigns those
* threads to device-visible CPUs; simulation uses the value directly.
* 1 orchestrator + 4 schedulers. Keep this distinct from the active upper
* bound even while both values are 5; the two settings have different runtime
* semantics and match the A2/A3 platform contract.
*/
constexpr int PLATFORM_DEFAULT_AICPU_THREAD_NUM = 5; // 1 orch + 4 sched

Expand All @@ -65,10 +66,10 @@ constexpr int PLATFORM_DEFAULT_AICPU_THREAD_NUM = 5; // 1 orch + 4 sched
* popcount(OCCUPY), and DeviceRunner passes that count (not this
* constant) to rtsLaunchCpuKernel.
*
* The bound exists for the static gate buffers in
* src/common/platform/onboard/aicpu/platform_aicpu_affinity.cpp
* (s_filter_thread_cpu[MAX_GATE_THREADS] etc.) — keep it ≥ any
* runtime aicpu_launch_count we expect to see.
* The bound sizes the per-launched-thread phase/task-timing storage and rejects
* a runtime launch population beyond the supported A5 topology. The common
* affinity gate has its own ABI bound, MAX_GATE_THREADS=16; keep this platform
* limit at or below that independent common bound.
*
* Two over-subscription failure modes to keep in mind when choosing
* the runtime launch count vs this bound:
Expand Down
2 changes: 2 additions & 0 deletions src/a5/platform/onboard/aicpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ target_link_directories(aicpu_kernel
${ASCEND_HOME_PATH}/lib64
)

target_link_libraries(aicpu_kernel PRIVATE ascend_hal)

# Output name
set_target_properties(aicpu_kernel PROPERTIES OUTPUT_NAME aicpu_kernel)

Expand Down
19 changes: 19 additions & 0 deletions src/a5/platform/onboard/aicpu/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*/
#include <cstdio>

#include <driver/ascend_hal_base.h>

#include "common/unified_log.h"
#include "common/kernel_args.h"
#include "common/platform_config.h"
Expand Down Expand Up @@ -41,6 +43,23 @@
// exported directly by the TMARB runtime (host_build_graph does not export it).
extern "C" int aicpu_execute(Runtime *arg);

extern "C" __attribute__((visibility("default"))) int simpler_aicpu_query_topology(void *arg) {
if (arg == nullptr) return -1;
auto *query_args = reinterpret_cast<AicpuTopologyQueryArgs *>(arg);
if (query_args->result_addr == 0) return -1;
auto *result = reinterpret_cast<AicpuTopologyQueryResult *>(query_args->result_addr);
int64_t value = 0;
result->occupy_rc = halGetDeviceInfo(0, MODULE_TYPE_AICPU, INFO_TYPE_OCCUPY, &value);
result->occupy = static_cast<uint64_t>(value);
value = 0;
result->pf_occupy_rc = halGetDeviceInfo(0, MODULE_TYPE_AICPU, INFO_TYPE_PF_OCCUPY, &value);
result->pf_occupy = static_cast<uint64_t>(value);
value = 0;
result->os_sched_rc = halGetDeviceInfo(0, MODULE_TYPE_AICPU, INFO_TYPE_OS_SCHED, &value);
result->os_sched = static_cast<uint64_t>(value);
return result->occupy_rc == 0 ? 0 : -1;
}

/**
* AICPU kernel main execution entry point.
*
Expand Down
5 changes: 5 additions & 0 deletions src/a5/platform/onboard/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ list(LENGTH HOST_RUNTIME_SOURCES NUM_HOST_RUNTIME_SOURCES)
message(STATUS "Host runtime: ${NUM_HOST_RUNTIME_SOURCES} source files")
message(VERBOSE "Host runtime sources: ${HOST_RUNTIME_SOURCES}")
add_library(host_runtime SHARED ${HOST_RUNTIME_SOURCES})
add_custom_command(TARGET host_runtime POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/aicpu_cpu_topo_fallback.json"
"$<TARGET_FILE_DIR:host_runtime>/aicpu_cpu_topo_fallback.json"
)

# C++ standard (applied only to C++ files)
set_target_properties(host_runtime PROPERTIES
Expand Down
31 changes: 31 additions & 0 deletions src/a5/platform/onboard/host/aicpu_cpu_topo_fallback.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"_comment": "Ascend950PR_9599 CPU_TOPO verified by live DSMI dump on 2026-08-05 (CANN 9.2.0). Full 9-logical layout kept for FG cluster/die classification; only role=compute entries are O/S-schedulable (device OCCUPY=0x1f8). cpu0=Control/OS_SCHED, cpu1+2=Data SMT pair on phy1. role/available are documentation fields ignored by the loader; occupy_mask is the match gate. Used only when live CPU_TOPO is unavailable.",
"socs": {
"Ascend950PR_9599": {
"occupy_mask": 504,
"cpus": [
{"cpu_id": 0, "phy_cpu_id": 0, "hyperthread_id": 0, "role": "control", "available": false},
{"cpu_id": 1, "phy_cpu_id": 1, "hyperthread_id": 0, "role": "data", "available": false},
{"cpu_id": 2, "phy_cpu_id": 1, "hyperthread_id": 1, "role": "data", "available": false},
{"cpu_id": 3, "phy_cpu_id": 2, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 4, "phy_cpu_id": 3, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 5, "phy_cpu_id": 4, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 6, "phy_cpu_id": 5, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 7, "phy_cpu_id": 6, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 8, "phy_cpu_id": 7, "hyperthread_id": 0, "role": "compute", "available": true}
]
},
"Ascend950PR_9579": {
"host_arch": "x86_64",
"occupy_mask": 62,
"selection_policy": "generic",
"cpus": [
{"cpu_id": 1, "phy_cpu_id": 1, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 2, "phy_cpu_id": 2, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 3, "phy_cpu_id": 3, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 4, "phy_cpu_id": 4, "hyperthread_id": 0, "role": "compute", "available": true},
{"cpu_id": 5, "phy_cpu_id": 5, "hyperthread_id": 0, "role": "compute", "available": true}
]
}
}
}
Loading
Loading