Skip to content
Merged
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
34 changes: 13 additions & 21 deletions docs/dfx/l2-swimlane-profiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ available.
Enable in one line:

```bash
python tests/st/<case>/test_<name>.py -p a2a3 -d 0 --enable-l2-swimlane
python tests/st/<case>/test_<name>.py -p <platform> -d 0 --enable-l2-swimlane
```

## 3. How to Use
Expand All @@ -62,31 +62,23 @@ backward-compatible with the old boolean behavior).
| 3 | + Scheduler phases (`SCHED_*`) | Skips orchestrator phases |
| 4 | + Orchestrator phases | Full collection |

> **Platform scope.** The tiered perf_level is currently
> implemented on **a2a3 only**. The a5 backend (both `a5` onboard
> and `a5sim`) has not been updated and still interprets
> `--enable-l2-swimlane` as a plain boolean: bare flag = on,
> absent = off. Passing an integer to a5 is silently treated as
> "on" regardless of value.

```bash
# Standalone runner — full collection (level 4)
python tests/st/<case>/test_<name>.py -p a2a3 -d 0 --enable-l2-swimlane

# Standalone runner — AICore timing only (level 1)
python tests/st/<case>/test_<name>.py -p a2a3 -d 0 --enable-l2-swimlane 1
# Standalone runner
python tests/st/<case>/test_<name>.py -p <platform> -d 0 --enable-l2-swimlane [PERF_LEVEL]

# Standalone runner — per-task with dispatch/fanout (level 2)
python tests/st/<case>/test_<name>.py -p a2a3 -d 0 --enable-l2-swimlane 2
# pytest — same flag shape
pytest tests/st/<case> --platform <platform> -d 0 --enable-l2-swimlane [PERF_LEVEL]

# pytest — scheduler phases (level 3)
pytest tests/st/<case> --platform a2a3 -d 0 --enable-l2-swimlane 3

# a5 onboard / a5sim — boolean only (perf_level integer not honored on a5 yet)
python tests/st/<case>/test_<name>.py -p a5 -d 0 --enable-l2-swimlane
python tests/st/<case>/test_<name>.py -p a5sim --enable-l2-swimlane
# Bare flag (no integer) — shorthand for level 4 (full collection)
python tests/st/<case>/test_<name>.py -p <platform> -d 0 --enable-l2-swimlane
```

- `<platform>` — one of `a2a3` / `a2a3sim` / `a5` / `a5sim`; the
integer perf_level interface is identical across them.
- `[PERF_LEVEL]` — optional integer 0–4 (see table above). Omit the
argument entirely (bare `--enable-l2-swimlane`) for the level-4
shorthand; omit the flag entirely for level 0 (disabled).

The flag sets `CallConfig::enable_l2_swimlane` to the chosen
level. The host then allocates the per-core / per-thread shared
region and publishes its base address through
Expand Down
8 changes: 5 additions & 3 deletions docs/task-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,11 @@ View does **not** own memory. Valid for the duration of a single
struct CallConfig {
int32_t block_dim = 0; // 0 = auto (DeviceRunner resolves to stream max at run() time)
int32_t aicpu_thread_num = 3;
bool enable_l2_swimlane = false;
bool enable_dump_tensor = false;
int32_t enable_pmu = 0;
int32_t enable_l2_swimlane = 0; // perf_level 0–4 (0=off, 4=full)
int32_t enable_dump_tensor = 0;
int32_t enable_pmu = 0; // 0 = disabled; >0 selects PMU event type
int32_t enable_dep_gen = 0;
char output_prefix[1024] = {};
// future fields here - same POD used at all levels
};
```
Expand Down
2 changes: 1 addition & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ python test_xxx.py -p a2a3sim --log-level debug # verbose C++ l
| `--case SEL` | | (all) | Case selector, repeatable: `Foo`, `ClassA::Foo`, `ClassA::` |
| `--manual` | | `exclude` | `exclude`/`include`/`only` for manual cases |
| `--skip-golden` | | false | Skip golden comparison (for benchmarking) |
| `--enable-l2-swimlane [PERF_LEVEL]` | | `0` | Enable L2 swimlane collection on first round only. On a2a3 the flag takes an integer perf_level 0–4 (bare = 4); see [docs/dfx/l2-swimlane-profiling.md](dfx/l2-swimlane-profiling.md#31-enable-l2-swimlane) for the level table. On a5 (both `a5` onboard and `a5sim`) the perf_level integer is not honored yet — the flag is treated as boolean (bare = on). Each test case gets its own `outputs/<case>_<ts>/` directory under which `l2_perf_records.json` lands; parallel runs never collide. |
| `--enable-l2-swimlane [PERF_LEVEL]` | | `0` | Enable L2 swimlane collection on first round only. The flag takes an integer perf_level 0–4 (bare = 4); see [docs/dfx/l2-swimlane-profiling.md](dfx/l2-swimlane-profiling.md#31-enable-l2-swimlane) for the level table. Each test case gets its own `outputs/<case>_<ts>/` directory under which `l2_perf_records.json` lands; parallel runs never collide. |
| `--dump-tensor` | | false | Dump per-task tensor I/O during runtime execution |
| `--enable-pmu [EVENT_TYPE]` | | `0` | Enable a2a3 PMU CSV collection. Bare flag selects `PIPE_UTILIZATION` (`2`); pass an event type such as `4` for `MEMORY`. |
| `--build` | | false | Compile runtime from source (not pre-built) |
Expand Down
15 changes: 14 additions & 1 deletion src/a5/platform/include/aicpu/l2_perf_collector_aicpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,24 @@
* L2 perf platform setters — called by the host (sim) or the AICPU kernel
* entry (onboard) before `l2_perf_aicpu_init()` so AICPU code can read perf
* state without reaching into the generic `Runtime` struct.
*
* Two-channel level transport (mirrors the PMU pattern):
* - binary on/off — `enable_profiling_flag` bit1 → `set_l2_swimlane_enabled(bool)`
* at kernel entry; queried via `is_l2_swimlane_enabled()`.
* - granular L2PerfLevel — `L2PerfDataHeader::l2_perf_level` (shared memory);
* read in `l2_perf_aicpu_init` and cached, then queried via
* `get_l2_perf_level()` for `>= AICPU_TIMING / SCHED_PHASES / ORCH_PHASES` gates.
*/
extern "C" void set_platform_l2_perf_base(uint64_t l2_perf_data_base);
extern "C" uint64_t get_platform_l2_perf_base();
extern "C" void set_l2_swimlane_enabled(bool enable);
extern "C" bool is_l2_swimlane_enabled();

// Typed getter for the granular perf_level (promoted from the shared-memory
// header inside l2_perf_aicpu_init). Gate sites should use this so the
// comparison RHS is a named L2PerfLevel constant.
L2PerfLevel get_l2_perf_level();

/**
* Initialize performance profiling for `worker_count` cores.
*
Expand All @@ -63,6 +75,7 @@ void l2_perf_aicpu_init(int worker_count);
* runtime linked-list types).
*
* @param core_id Core ID owning the destination buffer (resolved via s_perf_buffer_states)
* @param thread_idx Owning AICPU thread (used when rotating records buffer)
* @param expected_reg_task_id Register dispatch token (low 32 bits) to validate
* @param task_id Task identifier to write (PTO2 encoding or plain id)
* @param func_id Kernel function identifier
Expand All @@ -77,7 +90,7 @@ void l2_perf_aicpu_init(int worker_count);
* (they take the dropped path). Same shape as a2a3.
*/
int l2_perf_aicpu_complete_record(
int core_id, uint32_t expected_reg_task_id, uint64_t task_id, uint32_t func_id, CoreType core_type,
int core_id, int thread_idx, uint32_t expected_reg_task_id, uint64_t task_id, uint32_t func_id, CoreType core_type,
uint64_t dispatch_time, uint64_t finish_time, const uint64_t *fanout, int32_t fanout_count
);

Expand Down
27 changes: 26 additions & 1 deletion src/a5/platform/include/common/l2_perf_profiling.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,28 @@
#define RUNTIME_MAX_FANOUT 128
#endif

// =============================================================================
// L2 perf_level — granularity ladder for the L2 swimlane profiler.
//
// Each level is a strict superset of the previous: higher levels add the data
// described by their name on top of all lower-level data. Naming describes
// what is NEWLY captured at that level (incremental view), so gate sites read
// naturally — e.g. `if (level >= SCHED_PHASES)` means "this section runs when
// scheduler phase records are being collected (or any higher tier)".
//
// Transported via `L2PerfDataHeader::l2_perf_level` (host → AICPU,
// shared memory) and `CallConfig::enable_l2_swimlane` (Python → C). The wire
// representation stays integer (uint32_t / int32_t) for ABI stability; this
// enum is the canonical in-code type used for comparisons.
// =============================================================================
enum class L2PerfLevel : uint32_t {
DISABLED = 0, // No collection at all
AICORE_TIMING = 1, // AICore per-task start/end timestamps + task record buffer
AICPU_TIMING = 2, // + AICPU dispatch/finish timestamps + fanout dependency list
SCHED_PHASES = 3, // + scheduler main-loop phase records (SCHED_COMPLETE/DISPATCH/IDLE_WAIT)
ORCH_PHASES = 4, // + orchestrator phase records
};

// =============================================================================
// L2PerfRecord - Single Task Execution Record
// =============================================================================
Expand Down Expand Up @@ -265,7 +287,10 @@ struct L2PerfDataHeader {
volatile uint32_t queue_tails[PLATFORM_MAX_AICPU_THREADS]; // Producer write positions (AICPU modifies)

// Metadata (Host initializes, Device read-only)
uint32_t num_cores; // Actual number of cores launched
uint32_t num_cores; // Actual number of cores launched
uint32_t l2_perf_level; // 0=off, 1=AICore timing, 2=+dispatch/fanout,
// 3=+sched phases, 4=+orch phases. Host writes
// at init; AICPU reads in l2_perf_aicpu_init.
} __attribute__((aligned(64)));

// =============================================================================
Expand Down
12 changes: 10 additions & 2 deletions src/a5/platform/include/host/l2_perf_collector.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,13 @@ class L2PerfCollector : public profiling_common::ProfilerBase<L2PerfCollector, L
*
* @param num_aicore Number of AICore instances
* @param device_id Device ID (forwarded to register_cb)
* @param l2_perf_level Collection granularity (DISABLED / AICORE_TIMING
* / AICPU_TIMING / SCHED_PHASES / ORCH_PHASES).
* Written into `L2PerfDataHeader::l2_perf_level`
* so AICPU can promote it in `l2_perf_aicpu_init`,
* AND cached on the collector so
* `export_swimlane_json()` can gate phase sections
* and stamp the JSON `version`.
* @param alloc_cb Device memory allocation callback
* @param register_cb Memory registration callback (nullptr on a5 ⇒
* host-shadow allocation via malloc)
Expand All @@ -234,8 +241,8 @@ class L2PerfCollector : public profiling_common::ProfilerBase<L2PerfCollector, L
* @return 0 on success, error code on failure
*/
int initialize(
int num_aicore, int device_id, const L2PerfAllocCallback &alloc_cb, L2PerfRegisterCallback register_cb,
const L2PerfFreeCallback &free_cb, const std::string &output_prefix
int num_aicore, int device_id, L2PerfLevel l2_perf_level, const L2PerfAllocCallback &alloc_cb,
L2PerfRegisterCallback register_cb, const L2PerfFreeCallback &free_cb, const std::string &output_prefix
);

/**
Expand Down Expand Up @@ -327,6 +334,7 @@ class L2PerfCollector : public profiling_common::ProfilerBase<L2PerfCollector, L
void *aicore_ring_addrs_host_{nullptr};

int num_aicore_{0};
L2PerfLevel l2_perf_level_{L2PerfLevel::DISABLED};

// Per-task output directory captured at initialize() time. Consumed by
// export_swimlane_json() to build <prefix>/l2_perf_records.json.
Expand Down
2 changes: 1 addition & 1 deletion src/a5/platform/onboard/host/device_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ void DeviceRunner::finalize_collectors() {

int DeviceRunner::init_l2_perf(int num_aicore, int device_id) {
int rc = l2_perf_collector_.initialize(
num_aicore, device_id, prof_alloc_cb, /*register_cb=*/nullptr, prof_free_cb, output_prefix_
num_aicore, device_id, l2_perf_level_, prof_alloc_cb, /*register_cb=*/nullptr, prof_free_cb, output_prefix_
);
if (rc == 0) {
kernel_args_.args.l2_perf_data_base =
Expand Down
6 changes: 5 additions & 1 deletion src/a5/platform/onboard/host/device_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,10 @@ class DeviceRunner {
* corresponding `enable_*_` members directly. Moved off the generic
* Runtime struct / run() arg list so all three travel the same way.
*/
void set_l2_swimlane_enabled(bool enable) { enable_l2_swimlane_ = enable; }
void set_l2_swimlane_enabled(int level) {
l2_perf_level_ = static_cast<L2PerfLevel>(level);
enable_l2_swimlane_ = (l2_perf_level_ != L2PerfLevel::DISABLED);
}
void set_dump_tensor_enabled(bool enable) { enable_dump_tensor_ = enable; }
void set_pmu_enabled(int enable_pmu) {
enable_pmu_ = (enable_pmu > 0);
Expand Down Expand Up @@ -666,6 +669,7 @@ class DeviceRunner {
bool enable_l2_swimlane_{false};
bool enable_dump_tensor_{false};
bool enable_pmu_{false};
L2PerfLevel l2_perf_level_{L2PerfLevel::DISABLED}; // resolved from set_l2_swimlane_enabled()
PmuEventType pmu_event_type_{PmuEventType::PIPE_UTILIZATION}; // resolved from set_pmu_enabled()
std::string output_prefix_{}; // diagnostic artifact root directory

Expand Down
2 changes: 1 addition & 1 deletion src/a5/platform/onboard/host/pto_runtime_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ int run_prepared(
return rc;
}

runner->set_l2_swimlane_enabled(enable_l2_swimlane != 0);
runner->set_l2_swimlane_enabled(enable_l2_swimlane);
runner->set_dump_tensor_enabled(enable_dump_tensor != 0);
runner->set_pmu_enabled(enable_pmu);
runner->set_output_prefix(output_prefix);
Expand Down
2 changes: 1 addition & 1 deletion src/a5/platform/sim/host/device_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ void DeviceRunner::finalize_collectors() {

int DeviceRunner::init_l2_perf(int num_aicore, int device_id) {
int rc = l2_perf_collector_.initialize(
num_aicore, device_id, prof_alloc_cb, /*register_cb=*/nullptr, prof_free_cb, output_prefix_
num_aicore, device_id, l2_perf_level_, prof_alloc_cb, /*register_cb=*/nullptr, prof_free_cb, output_prefix_
);
if (rc == 0) {
kernel_args_.l2_perf_data_base = reinterpret_cast<uint64_t>(l2_perf_collector_.get_l2_perf_setup_device_ptr());
Expand Down
6 changes: 5 additions & 1 deletion src/a5/platform/sim/host/device_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ class DeviceRunner {
* corresponding `enable_*_` members directly. Moved off the generic
* Runtime struct / run() arg list so all three travel the same way.
*/
void set_l2_swimlane_enabled(bool enable) { enable_l2_swimlane_ = enable; }
void set_l2_swimlane_enabled(int level) {
l2_perf_level_ = static_cast<L2PerfLevel>(level);
enable_l2_swimlane_ = (l2_perf_level_ != L2PerfLevel::DISABLED);
}
void set_dump_tensor_enabled(bool enable) { enable_dump_tensor_ = enable; }
void set_pmu_enabled(int enable_pmu) {
enable_pmu_ = (enable_pmu > 0);
Expand Down Expand Up @@ -413,6 +416,7 @@ class DeviceRunner {
bool enable_l2_swimlane_{false};
bool enable_dump_tensor_{false};
bool enable_pmu_{false};
L2PerfLevel l2_perf_level_{L2PerfLevel::DISABLED}; // resolved from set_l2_swimlane_enabled()
PmuEventType pmu_event_type_{PmuEventType::PIPE_UTILIZATION}; // resolved from set_pmu_enabled()
std::string output_prefix_{}; // diagnostic artifact root directory

Expand Down
2 changes: 1 addition & 1 deletion src/a5/platform/sim/host/pto_runtime_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ int run_prepared(
return rc;
}

runner->set_l2_swimlane_enabled(enable_l2_swimlane != 0);
runner->set_l2_swimlane_enabled(enable_l2_swimlane);
runner->set_dump_tensor_enabled(enable_dump_tensor != 0);
runner->set_pmu_enabled(enable_pmu);
runner->set_output_prefix(output_prefix);
Expand Down
Loading
Loading