Skip to content

Refactor(a5): align profiling stack with a2a3 (host CRTP + stable ring) - #777

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoZheng109:refactor/a5-profiling-framework-alignment
May 18, 2026
Merged

ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoZheng109:refactor/a5-profiling-framework-alignment

Conversation

@ChaoZheng109

@ChaoZheng109 ChaoZheng109 commented May 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Brings a5's profiling stack to the same shape as a2a3 (PRs #705 / #709 / #714):

  • Host CRTP framework: L2PerfCollector / PmuCollector / TensorDumpCollector now derive from ProfilerBase<Derived, Module> over a shared BufferPoolManager<Module> under src/a5/platform/include/host/profiling_common/. Mgmt + poll threads + buffer pool logic deduplicated across three collectors.
  • Stable AICore staging ring: AICore writes per-task records to a per-core L2PerfAicoreRing / PmuAicoreRing allocated once by the host; address is published via KernelArgs::aicore_*_ring_addrs[block_idx] and never reassigned. Decouples AICore writes from AICPU's rotating records buffer, and fixes the latent PMU buffer-flip bug where AICore kept writing to the stale buffer.
  • Profiling out of Handshake: enablement bits + per-core address arrays moved from runtime Handshake to KernelArgs + AICore platform-owned slots (aicore_profiling_state.h). Adding a new profiling field no longer touches the runtime sync protocol. AICore resolves its PMU MMIO base directly from the existing KernelArgs::regs table at kernel entry (regs[get_physical_core_id()]), so there is no separate per-core pmu_reg_addrs table and no AICPU-fill-after-handshake dependency — the reg base is stable from Phase 1, and aicore_execute caches it at Phase 3 alongside the rings.
  • Three-bucket reconcile: L2PerfBufferState and PmuBufferState now track total / dropped / mismatch; collectors cross-check collected + dropped + mismatch == device_total per pool, matching a2a3.

a5's transport channel still differs from a2a3 (no halHostRegister on DAV_3510). The framework absorbs that as:

  • MemoryOps carries 5 callbacks (adds copy_to_device / copy_from_device)
  • mgmt loop mirrors the shm region per tick via profiling_copy.h
  • release_owned_buffers frees the paired host-shadow malloc()

Docs

  • docs/profiling-framework.md §8 — new a5-specifics section
  • docs/dfx/{pmu-profiling,l2-swimlane-profiling,tensor-dump}.md §5.x — a5 transport channel + lifecycle + reconcile semantics updated to match the code as it stands

Naming note

a5's new KernelArgs fields are named *_addrs (plural) for the per-core address arrays: aicore_l2_perf_ring_addrs, aicore_pmu_ring_addrs. a2a3's aicore_ring_addr (singular but pointing to an array) is logged for a follow-up a2a3 rename PR; not touched here per the arch-independence rule.

Test plan

  • python -m simpler_setup.build_runtimes --platform a5sim — host_build_graph + tensormap_and_ringbuffer build clean
  • a5sim CI (./ci.sh -p a5sim)
  • a5 hardware smoke
  • Verify reconcile equation collected + dropped + mismatch == device_total matches per pool on a representative case

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the profiling framework for the a5 architecture, centralizing host-side infrastructure for PMU, L2Perf, and TensorDump collectors. Key architectural changes include migrating profiling state from the runtime handshake to KernelArgs, introducing stable AICore staging rings to decouple writes from buffer rotations, and implementing a host-shadow transport mechanism to support systems without SVM. Review feedback recommends adding warning logs for null headers during buffer switches and utilizing store barriers when initializing hardware registers to prevent race conditions on weak memory model architectures.

Comment thread src/a5/platform/src/aicpu/l2_perf_collector_aicpu.cpp
Comment thread src/a5/platform/src/aicpu/pmu_collector_aicpu.cpp
@ChaoZheng109
ChaoZheng109 force-pushed the refactor/a5-profiling-framework-alignment branch 4 times, most recently from c0630d2 to 0572269 Compare May 18, 2026 02:52
…ICore ring, profiling off Handshake)

Brings a5 to the same shape as a2a3's PR [hw-native-sys#705](hw-native-sys#705) / [hw-native-sys#709](hw-native-sys#709) / [hw-native-sys#714](hw-native-sys#714): host-side
collectors share a ProfilerBase<Derived, Module> + BufferPoolManager<Module>
framework; AICore writes through a stable per-core L2PerfAicoreRing /
PmuAicoreRing decoupled from AICPU buffer rotation; profiling state moves
off Handshake onto KernelArgs + AICore platform-owned slots
(aicore_profiling_state.h). a5's transport channel deviates only in
MemoryOps carrying copy_to_device / copy_from_device, the mgmt loop
mirroring the shm region per tick, and release_owned_buffers freeing the
paired host shadow.

AICore now resolves its own PMU MMIO base at kernel entry directly from
KernelArgs::regs[get_physical_core_id()] (the per-physical-core
register-base table the host already fills for AICPU), instead of
indexing a separate pmu_reg_addrs table that AICPU filled during
handshake. The resolved base is valid from Phase 1 onward, so
aicore_execute caches it at Phase 3 alongside the rings rather than
re-reading per PMU record. Drops KernelArgs::pmu_reg_addrs,
set/get_platform_pmu_reg_addrs, and the corresponding host-side table
allocation in PmuCollector.
@ChaoZheng109
ChaoZheng109 force-pushed the refactor/a5-profiling-framework-alignment branch from 0572269 to a5a594b Compare May 18, 2026 03:00
@ChaoWao
ChaoWao merged commit cb8fc14 into hw-native-sys:main May 18, 2026
14 checks passed
indigo1973 added a commit to indigo1973/simpler that referenced this pull request May 21, 2026
Port the tiered L2 swimlane perf_level feature from hw-native-sys#782 (a2a3-only)
to the a5 platform, so a5 onboard and a5sim now honor the integer
perf_level (0-4) instead of treating --enable-l2-swimlane as a plain
boolean.

Mirror the a2a3 wiring on a5:
- L2PerfDataHeader::l2_perf_level carries the level into shared memory;
  AICPU promotes it in l2_perf_aicpu_init and exposes it via
  get_l2_perf_level().
- Host-side L2PerfCollector caches the level to gate JSON sections and
  stamps the JSON "version" field directly from perf_level.
- Apply level gates throughout AICPU code paths: skip dispatch/finish
  timestamps and fanout copies below AICPU_TIMING, scheduler phase
  records below SCHED_PHASES, and orchestrator phase records below
  ORCH_PHASES.
- Plumb perf_level through DeviceRunner / pto_runtime_c_api on both
  onboard and sim backends.
- Move l2_perf_aicpu_init out of the dispatch one-time-init block into
  SchedulerContext::init() in scheduler_cold_path.cpp, matching a2a3
  so the orchestrator thread reads a promoted level when caching
  rt->orchestrator.l2_perf_level.
- Remove the end-of-function wmb() in l2_perf_aicpu_record_phase
  (introduced by hw-native-sys#777, not present in a2a3). Measured ~1.1 ms
  reduction in L4 orch_cost on paged_attention_unroll Case1.

Update docs (l2-swimlane-profiling.md, profiling_levels.md,
testing.md) to drop the "a5 is boolean-only" caveat and document the
unified integer interface across a2a3 and a5.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request May 22, 2026
Port the tiered L2 swimlane perf_level feature from hw-native-sys#782 (a2a3-only)
to the a5 platform, so a5 onboard and a5sim now honor the integer
perf_level (0-4) instead of treating --enable-l2-swimlane as a plain
boolean.

Mirror the a2a3 wiring on a5:
- L2PerfDataHeader::l2_perf_level carries the level into shared memory;
  AICPU promotes it in l2_perf_aicpu_init and exposes it via
  get_l2_perf_level().
- Host-side L2PerfCollector caches the level to gate JSON sections and
  stamps the JSON "version" field directly from perf_level.
- Apply level gates throughout AICPU code paths: skip dispatch/finish
  timestamps and fanout copies below AICPU_TIMING, scheduler phase
  records below SCHED_PHASES, and orchestrator phase records below
  ORCH_PHASES.
- Plumb perf_level through DeviceRunner / pto_runtime_c_api on both
  onboard and sim backends.
- Move l2_perf_aicpu_init out of the dispatch one-time-init block into
  SchedulerContext::init() in scheduler_cold_path.cpp, matching a2a3
  so the orchestrator thread reads a promoted level when caching
  rt->orchestrator.l2_perf_level.
- Align l2_perf_aicpu_record_phase to a2a3 byte-for-byte: remove the
  end-of-function wmb() and the 3 dropped-path wmbs (all introduced
  by hw-native-sys#777, none present in a2a3), and unify the accounting comment
  + brace style. Measured ~1.1 ms reduction in L4 orch_cost on
  paged_attention_unroll Case1.

Update docs (l2-swimlane-profiling.md, profiling_levels.md,
testing.md) to drop the "a5 is boolean-only" caveat and document the
unified integer interface across a2a3 and a5.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request May 25, 2026
Port the tiered L2 swimlane perf_level feature from hw-native-sys#782 (a2a3-only)
to the a5 platform, so a5 onboard and a5sim now honor the integer
perf_level (0-4) instead of treating --enable-l2-swimlane as a plain
boolean.

Mirror the a2a3 wiring on a5:
- L2PerfDataHeader::l2_perf_level carries the level into shared memory;
  AICPU promotes it in l2_perf_aicpu_init and exposes it via
  get_l2_perf_level().
- Host-side L2PerfCollector caches the level to gate JSON sections and
  stamps the JSON "version" field directly from perf_level.
- Apply level gates throughout AICPU code paths: skip dispatch/finish
  timestamps and fanout copies below AICPU_TIMING, scheduler phase
  records below SCHED_PHASES, and orchestrator phase records below
  ORCH_PHASES.
- Plumb perf_level through DeviceRunner / pto_runtime_c_api on both
  onboard and sim backends.
- Move l2_perf_aicpu_init out of the dispatch one-time-init block into
  SchedulerContext::init() in scheduler_cold_path.cpp, matching a2a3
  so the orchestrator thread reads a promoted level when caching
  rt->orchestrator.l2_perf_level.
- Align l2_perf_aicpu_record_phase to a2a3 byte-for-byte: remove the
  end-of-function wmb() and the 3 dropped-path wmbs (all introduced
  by hw-native-sys#777, none present in a2a3), and unify the accounting comment
  + brace style. Measured ~1.1 ms reduction in L4 orch_cost on
  paged_attention_unroll Case1.
- Align l2_perf_aicpu_complete_record with a2a3: add thread_idx
  parameter (routed from both host_build_graph and tensormap_and_ringbuffer
  callers), introduce an AICPU-private s_perf_records_buffers[] cache as
  the records-buffer SoT, rename switch_buffer -> switch_records_buffer
  and rotate after the write so the just-committed record is preserved,
  and surface ring/task_id mismatch as a dedicated LOG_ERROR
  (completion-before-dispatch invariant violation) separate from
  capacity drops. init / flush_buffers maintain s_perf_records_buffers[]
  in lockstep with state->current_buf_ptr so flush deterministically
  halts subsequent commits.

Update docs (l2-swimlane-profiling.md, profiling_levels.md,
testing.md) to drop the "a5 is boolean-only" caveat and document the
unified integer interface across a2a3 and a5.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request May 25, 2026
Port the tiered L2 swimlane perf_level feature from [hw-native-sys#782](hw-native-sys#782) (a2a3-only)
to the a5 platform, so a5 onboard and a5sim now honor the integer
perf_level (0-4) instead of treating --enable-l2-swimlane as a plain
boolean.

Mirror the a2a3 wiring on a5:
- L2PerfDataHeader::l2_perf_level carries the level into shared memory;
  AICPU promotes it in l2_perf_aicpu_init and exposes it via
  get_l2_perf_level().
- Host-side L2PerfCollector caches the level to gate JSON sections and
  stamps the JSON "version" field directly from perf_level.
- Apply level gates throughout AICPU code paths: skip dispatch/finish
  timestamps and fanout copies below AICPU_TIMING, scheduler phase
  records below SCHED_PHASES, and orchestrator phase records below
  ORCH_PHASES.
- Plumb perf_level through DeviceRunner / pto_runtime_c_api on both
  onboard and sim backends.
- Move l2_perf_aicpu_init out of the dispatch one-time-init block into
  SchedulerContext::init() in scheduler_cold_path.cpp, matching a2a3
  so the orchestrator thread reads a promoted level when caching
  rt->orchestrator.l2_perf_level.
- Align l2_perf_aicpu_record_phase to a2a3 byte-for-byte: remove the
  end-of-function wmb() and the 3 dropped-path wmbs (all introduced
  by [hw-native-sys#777](hw-native-sys#777), none present in a2a3), and unify the accounting comment
  + brace style. Measured ~1.1 ms reduction in L4 orch_cost on
  paged_attention_unroll Case1.
- Align l2_perf_aicpu_complete_record with a2a3: add thread_idx
  parameter (routed from both host_build_graph and tensormap_and_ringbuffer
  callers), introduce an AICPU-private s_perf_records_buffers[] cache as
  the records-buffer SoT, rename switch_buffer -> switch_records_buffer
  and rotate after the write so the just-committed record is preserved,
  and surface ring/task_id mismatch as a dedicated LOG_ERROR
  (completion-before-dispatch invariant violation) separate from
  capacity drops. init / flush_buffers maintain s_perf_records_buffers[]
  in lockstep with state->current_buf_ptr so flush deterministically
  halts subsequent commits.

Update docs (l2-swimlane-profiling.md, profiling_levels.md,
testing.md) to drop the "a5 is boolean-only" caveat and document the
unified integer interface across a2a3 and a5.
ChaoZheng109 pushed a commit that referenced this pull request May 25, 2026
Port the tiered L2 swimlane perf_level feature from [#782](#782) (a2a3-only)
to the a5 platform, so a5 onboard and a5sim now honor the integer
perf_level (0-4) instead of treating --enable-l2-swimlane as a plain
boolean.

Mirror the a2a3 wiring on a5:
- L2PerfDataHeader::l2_perf_level carries the level into shared memory;
  AICPU promotes it in l2_perf_aicpu_init and exposes it via
  get_l2_perf_level().
- Host-side L2PerfCollector caches the level to gate JSON sections and
  stamps the JSON "version" field directly from perf_level.
- Apply level gates throughout AICPU code paths: skip dispatch/finish
  timestamps and fanout copies below AICPU_TIMING, scheduler phase
  records below SCHED_PHASES, and orchestrator phase records below
  ORCH_PHASES.
- Plumb perf_level through DeviceRunner / pto_runtime_c_api on both
  onboard and sim backends.
- Move l2_perf_aicpu_init out of the dispatch one-time-init block into
  SchedulerContext::init() in scheduler_cold_path.cpp, matching a2a3
  so the orchestrator thread reads a promoted level when caching
  rt->orchestrator.l2_perf_level.
- Align l2_perf_aicpu_record_phase to a2a3 byte-for-byte: remove the
  end-of-function wmb() and the 3 dropped-path wmbs (all introduced
  by [#777](#777), none present in a2a3), and unify the accounting comment
  + brace style. Measured ~1.1 ms reduction in L4 orch_cost on
  paged_attention_unroll Case1.
- Align l2_perf_aicpu_complete_record with a2a3: add thread_idx
  parameter (routed from both host_build_graph and tensormap_and_ringbuffer
  callers), introduce an AICPU-private s_perf_records_buffers[] cache as
  the records-buffer SoT, rename switch_buffer -> switch_records_buffer
  and rotate after the write so the just-committed record is preserved,
  and surface ring/task_id mismatch as a dedicated LOG_ERROR
  (completion-before-dispatch invariant violation) separate from
  capacity drops. init / flush_buffers maintain s_perf_records_buffers[]
  in lockstep with state->current_buf_ptr so flush deterministically
  halts subsequent commits.

Update docs (l2-swimlane-profiling.md, profiling_levels.md,
testing.md) to drop the "a5 is boolean-only" caveat and document the
unified integer interface across a2a3 and a5.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request May 28, 2026
Port the dep_gen (SubmitTrace) feature from a2a3 to a5 so the
tensormap_and_ringbuffer runtime on a5 can produce deps.json and feed
flow events into swimlane_converter.py. Without this, --enable-dep-gen
was a no-op on a5 and merged_swimlane_*.json had no dependency arrows.

Reused from a2a3 verbatim (byte-identical):
  - Shared-memory ABI: common/dep_gen.h (DepGenRecord 2624 B, overflow
    chain, SPSC free_queue, per-thread ready_queue)
  - AICPU writer: aicpu/dep_gen_collector_aicpu.{h,cpp}
  - Runtime replay: runtime/tensormap_and_ringbuffer/host/dep_gen_replay
  - Orchestrator capture point + aicpu_executor lifecycle hooks
  - 5 platform_config constants + PROFILING_FLAG_DEP_GEN bit

Specialized for a5 (no SVM, see profiling_common diff vs a2a3):
  - dep_gen_collector.cpp uses alloc_single_buffer (malloc shadow +
    profiling_copy_to_device) instead of identity-mapping when
    register_cb is null — matches a5's PMU/L2Perf/Dump collectors.
  - Two-phase set_memory_context: callbacks first, then shm pointers
    once the region is committed, so start(tf) gates correctly.
  - reconcile_counters explicitly copy_from_device's the BufferState +
    current_buf before reading (mgmt thread is stopped by then).
  - finalize lets BufferPoolManager::clear_mappings() be the single
    source of truth for host-shadow lifetime — no per-collector dedup.

Sim path: dlsym set_platform_dep_gen_base / set_dep_gen_enabled out of
the AICPU .so and forward kernel_args.dep_gen_data_base + enable flag
at boot, mirroring the existing pmu / dump / l2_perf setters.

Onboard kernel.cpp adds two lines to forward dep_gen_data_base +
PROFILING_FLAG_DEP_GEN into the AICPU writer's globals, mirroring the
existing PMU / L2 / Dump setters.

c_api: run_prepared's enable_dep_gen parameter is no longer ignored —
wired to runner->set_dep_gen_enabled() on both onboard and sim.

Tests:
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen.py: 6-edge validation
    against vector_example orchestration (byte-identical to a2a3 — same
    expected edge set).
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen_chain.py: overflow chain
    regression for >64 explicit deps.

Docs:
  - docs/dfx/dep_gen.md: §8 Architecture Touchpoints now lists both
    platforms; "Currently a2a3 only" line removed.
  - src/a5/runtime/.../docs/profiling_levels.md: Code Locations point
    at src/a5/ (was stale src/a2a3/ refs from PR hw-native-sys#777 cleanup) and
    add a dep_gen entry.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request May 31, 2026
Port the dep_gen (SubmitTrace) feature from a2a3 to a5 so the
tensormap_and_ringbuffer runtime on a5 can produce deps.json and feed
flow events into swimlane_converter.py. Without this, --enable-dep-gen
was a no-op on a5 and merged_swimlane_*.json had no dependency arrows.

Reused from a2a3 verbatim (byte-identical):
  - Shared-memory ABI: common/dep_gen.h (DepGenRecord 2624 B, overflow
    chain, SPSC free_queue, per-thread ready_queue)
  - AICPU writer: aicpu/dep_gen_collector_aicpu.{h,cpp}
  - Runtime replay: runtime/tensormap_and_ringbuffer/host/dep_gen_replay
  - Orchestrator capture point + aicpu_executor lifecycle hooks
  - 5 platform_config constants + PROFILING_FLAG_DEP_GEN bit

Specialized for a5 (no SVM, see profiling_common diff vs a2a3):
  - dep_gen_collector.cpp uses alloc_single_buffer (malloc shadow +
    profiling_copy_to_device) instead of identity-mapping when
    register_cb is null — matches a5's PMU/L2Perf/Dump collectors.
  - Two-phase set_memory_context: callbacks first, then shm pointers
    once the region is committed, so start(tf) gates correctly.
  - reconcile_counters explicitly copy_from_device's the BufferState +
    current_buf before reading (mgmt thread is stopped by then).
  - finalize lets BufferPoolManager::clear_mappings() be the single
    source of truth for host-shadow lifetime — no per-collector dedup.

Sim path: dlsym set_platform_dep_gen_base / set_dep_gen_enabled out of
the AICPU .so and forward kernel_args.dep_gen_data_base + enable flag
at boot, mirroring the existing pmu / dump / l2_perf setters.

Onboard kernel.cpp adds two lines to forward dep_gen_data_base +
PROFILING_FLAG_DEP_GEN into the AICPU writer's globals, mirroring the
existing PMU / L2 / Dump setters.

c_api: run_prepared's enable_dep_gen parameter is no longer ignored —
wired to runner->set_dep_gen_enabled() on both onboard and sim.

Tests:
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen.py: 6-edge validation
    against vector_example orchestration (byte-identical to a2a3 — same
    expected edge set).
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen_chain.py: overflow chain
    regression for >64 explicit deps.

Docs:
  - docs/dfx/dep_gen.md: §8 Architecture Touchpoints now lists both
    platforms; "Currently a2a3 only" line removed.
  - src/a5/runtime/.../docs/profiling_levels.md: Code Locations point
    at src/a5/ (was stale src/a2a3/ refs from PR hw-native-sys#777 cleanup) and
    add a dep_gen entry.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request Jun 1, 2026
Port the dep_gen (SubmitTrace) feature from a2a3 to a5 so the
tensormap_and_ringbuffer runtime on a5 can produce deps.json and feed
flow events into swimlane_converter.py. Without this, --enable-dep-gen
was a no-op on a5 and merged_swimlane_*.json had no dependency arrows.

Reused from a2a3 verbatim (byte-identical):
  - Shared-memory ABI: common/dep_gen.h (DepGenRecord 2624 B, overflow
    chain, SPSC free_queue, per-thread ready_queue)
  - AICPU writer: aicpu/dep_gen_collector_aicpu.{h,cpp}
  - Runtime replay: runtime/tensormap_and_ringbuffer/host/dep_gen_replay
  - Orchestrator capture point + aicpu_executor lifecycle hooks
  - 5 platform_config constants + PROFILING_FLAG_DEP_GEN bit

Specialized for a5 (no SVM, see profiling_common diff vs a2a3):
  - dep_gen_collector.cpp uses alloc_single_buffer (malloc shadow +
    profiling_copy_to_device) instead of identity-mapping when
    register_cb is null — matches a5's PMU/L2Perf/Dump collectors.
  - Two-phase set_memory_context: callbacks first, then shm pointers
    once the region is committed, so start(tf) gates correctly.
  - reconcile_counters explicitly copy_from_device's the BufferState +
    current_buf before reading (mgmt thread is stopped by then).
  - finalize lets BufferPoolManager::clear_mappings() be the single
    source of truth for host-shadow lifetime — no per-collector dedup.

Sim path: dlsym set_platform_dep_gen_base / set_dep_gen_enabled out of
the AICPU .so and forward kernel_args.dep_gen_data_base + enable flag
at boot, mirroring the existing pmu / dump / l2_perf setters.

Onboard kernel.cpp adds two lines to forward dep_gen_data_base +
PROFILING_FLAG_DEP_GEN into the AICPU writer's globals, mirroring the
existing PMU / L2 / Dump setters.

c_api: run_prepared's enable_dep_gen parameter is no longer ignored —
wired to runner->set_dep_gen_enabled() on both onboard and sim.

Tests:
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen.py: 6-edge validation
    against vector_example orchestration (byte-identical to a2a3 — same
    expected edge set).
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen_chain.py: overflow chain
    regression for >64 explicit deps.

Docs:
  - docs/dfx/dep_gen.md: §8 Architecture Touchpoints now lists both
    platforms; "Currently a2a3 only" line removed.
  - src/a5/runtime/.../docs/profiling_levels.md: Code Locations point
    at src/a5/ (was stale src/a2a3/ refs from PR hw-native-sys#777 cleanup) and
    add a dep_gen entry.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request Jun 1, 2026
Port the dep_gen (SubmitTrace) feature from a2a3 to a5 so the
tensormap_and_ringbuffer runtime on a5 can produce deps.json and feed
flow events into swimlane_converter.py. Without this, --enable-dep-gen
was a no-op on a5 and merged_swimlane_*.json had no dependency arrows.

Reused from a2a3 (code-identical; the .cpp files are byte-for-byte
copies, the headers differ only in include-guard names and
L2Swimlane->L2Perf / a2a3->a5 comment refs):
  - Shared-memory ABI: common/dep_gen.h (DepGenRecord 2624 B, overflow
    chain, SPSC free_queue, per-thread ready_queue) — layout byte-identical
  - AICPU writer: aicpu/dep_gen_collector_aicpu.{h,cpp} (.cpp identical)
  - Runtime replay: runtime/tensormap_and_ringbuffer/host/dep_gen_replay
    (.cpp identical)
  - Orchestrator capture point + aicpu_executor lifecycle hooks
  - 5 platform_config constants + PROFILING_FLAG_DEP_GEN bit

Specialized for a5 (no SVM, see profiling_common diff vs a2a3):
  - dep_gen_collector.cpp uses alloc_single_buffer (malloc shadow +
    profiling_copy_to_device) instead of identity-mapping when
    register_cb is null — matches a5's PMU/L2Perf/Dump collectors.
  - Two-phase set_memory_context: callbacks first, then shm pointers
    once the region is committed, so start(tf) gates correctly.
  - reconcile_counters explicitly copy_from_device's the BufferState +
    current_buf before reading (mgmt thread is stopped by then).
  - finalize lets BufferPoolManager::clear_mappings() be the single
    source of truth for host-shadow lifetime — no per-collector dedup.

Sim path: dlsym set_platform_dep_gen_base / set_dep_gen_enabled out of
the AICPU .so and forward kernel_args.dep_gen_data_base + enable flag
at boot, mirroring the existing pmu / dump / l2_perf setters.

Onboard kernel.cpp adds two lines to forward dep_gen_data_base +
PROFILING_FLAG_DEP_GEN into the AICPU writer's globals, mirroring the
existing PMU / L2 / Dump setters.

c_api: a5's DeviceRunner now overrides set_dep_gen_enabled() (was the
base no-op), so run_prepared's already-present enable_dep_gen call is
honored on a5 instead of being silently dropped. The shared
c_api_shared.cpp / device_runner_base.h changes are comment-only —
refreshing stale "a2a3-only" notes to reflect both arches now wire it.

Tests:
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen.py: 6-edge validation
    against vector_example orchestration (byte-identical to a2a3 — same
    expected edge set).
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen_chain.py
    (+ kernels/orchestration/chain_barrier_orch.cpp): overflow chain
    regression for >64 explicit deps.
  - tests/st/a2a3/.../dfx/dep_gen/test_dep_gen.py: harden the existing
    a2a3 gate — a missing deps.json under --enable-dep-gen now fails
    loudly (assert) instead of silently skipping, so a
    capture->reconcile->replay regression can no longer pass green
    (hw-native-sys#742).

Docs:
  - docs/dfx/dep_gen.md: §8 Architecture Touchpoints now lists both
    platforms; "Currently a2a3 only" line removed.
  - src/a5/runtime/.../docs/profiling_levels.md: Code Locations point
    at src/a5/ (was stale src/a2a3/ refs from PR hw-native-sys#777 cleanup) and
    add a dep_gen entry.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request Jun 1, 2026
Port the dep_gen (SubmitTrace) feature from a2a3 to a5 so the
tensormap_and_ringbuffer runtime on a5 can produce deps.json and feed
flow events into swimlane_converter.py. Without this, --enable-dep-gen
was a no-op on a5 and merged_swimlane_*.json had no dependency arrows.

Reused from a2a3 (code-identical; the .cpp files are byte-for-byte
copies, the headers differ only in include-guard names and
L2Swimlane->L2Perf / a2a3->a5 comment refs):
  - Shared-memory ABI: common/dep_gen.h (DepGenRecord 2624 B, overflow
    chain, SPSC free_queue, per-thread ready_queue) — layout byte-identical
  - AICPU writer: aicpu/dep_gen_collector_aicpu.{h,cpp} (.cpp identical)
  - Runtime replay: runtime/tensormap_and_ringbuffer/host/dep_gen_replay
    (.cpp identical)
  - Orchestrator capture point + aicpu_executor lifecycle hooks
  - 5 platform_config constants + PROFILING_FLAG_DEP_GEN bit

Specialized for a5 (no SVM, see profiling_common diff vs a2a3):
  - dep_gen_collector.cpp uses alloc_single_buffer (malloc shadow +
    profiling_copy_to_device) instead of identity-mapping when
    register_cb is null — matches a5's PMU/L2Perf/Dump collectors.
  - Two-phase set_memory_context: callbacks first, then shm pointers
    once the region is committed, so start(tf) gates correctly.
  - reconcile_counters explicitly copy_from_device's the BufferState +
    current_buf before reading (mgmt thread is stopped by then).
  - finalize lets BufferPoolManager::clear_mappings() be the single
    source of truth for host-shadow lifetime — no per-collector dedup.

Sim path: dlsym set_platform_dep_gen_base / set_dep_gen_enabled out of
the AICPU .so and forward kernel_args.dep_gen_data_base + enable flag
at boot, mirroring the existing pmu / dump / l2_perf setters.

Onboard kernel.cpp adds two lines to forward dep_gen_data_base +
PROFILING_FLAG_DEP_GEN into the AICPU writer's globals, mirroring the
existing PMU / L2 / Dump setters.

c_api: a5's DeviceRunner now overrides set_dep_gen_enabled() (was the
base no-op), so run_prepared's already-present enable_dep_gen call is
honored on a5 instead of being silently dropped. The shared
c_api_shared.cpp / device_runner_base.h changes are comment-only —
refreshing stale "a2a3-only" notes to reflect both arches now wire it.

Tests:
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen.py: 6-edge validation
    against vector_example orchestration (byte-identical to a2a3 — same
    expected edge set).
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen_chain.py
    (+ kernels/orchestration/chain_barrier_orch.cpp): overflow chain
    regression for >64 explicit deps.
  - tests/st/a2a3/.../dfx/dep_gen/test_dep_gen.py: harden the existing
    a2a3 gate — a missing deps.json under --enable-dep-gen now fails
    loudly (assert) instead of silently skipping, so a
    capture->reconcile->replay regression can no longer pass green
    (hw-native-sys#742).

Docs:
  - docs/dfx/dep_gen.md: §8 Architecture Touchpoints now lists both
    platforms; "Currently a2a3 only" line removed.
  - src/a5/runtime/.../docs/profiling_levels.md: Code Locations point
    at src/a5/ (was stale src/a2a3/ refs from PR hw-native-sys#777 cleanup) and
    add a dep_gen entry.
indigo1973 added a commit to indigo1973/simpler that referenced this pull request Jun 1, 2026
Port the dep_gen (SubmitTrace) feature from a2a3 to a5 so the
tensormap_and_ringbuffer runtime on a5 can produce deps.json and feed
flow events into swimlane_converter.py. Without this, --enable-dep-gen
was a no-op on a5 and merged_swimlane_*.json had no dependency arrows.

Reused from a2a3 (code-identical; the .cpp files are byte-for-byte
copies, the headers differ only in include-guard names and
L2Swimlane->L2Perf / a2a3->a5 comment refs):
  - Shared-memory ABI: common/dep_gen.h (DepGenRecord 2624 B, overflow
    chain, SPSC free_queue, per-thread ready_queue) — layout byte-identical
  - AICPU writer: aicpu/dep_gen_collector_aicpu.{h,cpp} (.cpp identical)
  - Runtime replay: runtime/tensormap_and_ringbuffer/host/dep_gen_replay
    (.cpp identical)
  - Orchestrator capture point + aicpu_executor lifecycle hooks
  - 5 platform_config constants + PROFILING_FLAG_DEP_GEN bit

Specialized for a5 (no SVM, see profiling_common diff vs a2a3):
  - dep_gen_collector.cpp uses alloc_single_buffer (malloc shadow +
    profiling_copy_to_device) instead of identity-mapping when
    register_cb is null — matches a5's PMU/L2Perf/Dump collectors.
  - Two-phase set_memory_context: callbacks first, then shm pointers
    once the region is committed, so start(tf) gates correctly.
  - reconcile_counters explicitly copy_from_device's the BufferState +
    current_buf before reading (mgmt thread is stopped by then).
  - finalize lets BufferPoolManager::clear_mappings() be the single
    source of truth for host-shadow lifetime — no per-collector dedup.

Sim path: dlsym set_platform_dep_gen_base / set_dep_gen_enabled out of
the AICPU .so and forward kernel_args.dep_gen_data_base + enable flag
at boot, mirroring the existing pmu / dump / l2_perf setters.

Onboard kernel.cpp adds two lines to forward dep_gen_data_base +
PROFILING_FLAG_DEP_GEN into the AICPU writer's globals, mirroring the
existing PMU / L2 / Dump setters.

c_api: a5's DeviceRunner now overrides set_dep_gen_enabled() (was the
base no-op), so run_prepared's already-present enable_dep_gen call is
honored on a5 instead of being silently dropped. The shared
c_api_shared.cpp / device_runner_base.h changes are comment-only —
refreshing stale "a2a3-only" notes to reflect both arches now wire it.

Tests:
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen.py: 6-edge validation
    against vector_example orchestration (byte-identical to a2a3 — same
    expected edge set).
  - tests/st/a5/.../dfx/dep_gen/test_dep_gen_chain.py
    (+ kernels/orchestration/chain_barrier_orch.cpp): overflow chain
    regression for >64 explicit deps.
  - tests/st/a2a3/.../dfx/dep_gen/test_dep_gen.py: harden the existing
    a2a3 gate — a missing deps.json under --enable-dep-gen now fails
    loudly (assert) instead of silently skipping, so a
    capture->reconcile->replay regression can no longer pass green
    ([hw-native-sys#742](hw-native-sys#742)).

Docs:
  - docs/dfx/dep_gen.md: §8 Architecture Touchpoints now lists both
    platforms; "Currently a2a3 only" line removed.
  - src/a5/runtime/.../docs/profiling_levels.md: Code Locations point
    at src/a5/ (was stale src/a2a3/ refs from PR [hw-native-sys#777](hw-native-sys#777) cleanup) and
    add a dep_gen entry.
nalinaly pushed a commit to nalinaly/simpler that referenced this pull request Jul 31, 2026
…ICore ring, profiling off Handshake) (hw-native-sys#777)

Brings a5 to the same shape as a2a3's PR [hw-native-sys#705](hw-native-sys#705) / [hw-native-sys#709](hw-native-sys#709) / [hw-native-sys#714](hw-native-sys#714): host-side
collectors share a ProfilerBase<Derived, Module> + BufferPoolManager<Module>
framework; AICore writes through a stable per-core L2PerfAicoreRing /
PmuAicoreRing decoupled from AICPU buffer rotation; profiling state moves
off Handshake onto KernelArgs + AICore platform-owned slots
(aicore_profiling_state.h). a5's transport channel deviates only in
MemoryOps carrying copy_to_device / copy_from_device, the mgmt loop
mirroring the shm region per tick, and release_owned_buffers freeing the
paired host shadow.

AICore now resolves its own PMU MMIO base at kernel entry directly from
KernelArgs::regs[get_physical_core_id()] (the per-physical-core
register-base table the host already fills for AICPU), instead of
indexing a separate pmu_reg_addrs table that AICPU filled during
handshake. The resolved base is valid from Phase 1 onward, so
aicore_execute caches it at Phase 3 alongside the rings rather than
re-reading per PMU record. Drops KernelArgs::pmu_reg_addrs,
set/get_platform_pmu_reg_addrs, and the corresponding host-side table
allocation in PmuCollector.
nalinaly pushed a commit to nalinaly/simpler that referenced this pull request Jul 31, 2026
…e-sys#841)

Port the tiered L2 swimlane perf_level feature from [hw-native-sys#782](hw-native-sys#782) (a2a3-only)
to the a5 platform, so a5 onboard and a5sim now honor the integer
perf_level (0-4) instead of treating --enable-l2-swimlane as a plain
boolean.

Mirror the a2a3 wiring on a5:
- L2PerfDataHeader::l2_perf_level carries the level into shared memory;
  AICPU promotes it in l2_perf_aicpu_init and exposes it via
  get_l2_perf_level().
- Host-side L2PerfCollector caches the level to gate JSON sections and
  stamps the JSON "version" field directly from perf_level.
- Apply level gates throughout AICPU code paths: skip dispatch/finish
  timestamps and fanout copies below AICPU_TIMING, scheduler phase
  records below SCHED_PHASES, and orchestrator phase records below
  ORCH_PHASES.
- Plumb perf_level through DeviceRunner / pto_runtime_c_api on both
  onboard and sim backends.
- Move l2_perf_aicpu_init out of the dispatch one-time-init block into
  SchedulerContext::init() in scheduler_cold_path.cpp, matching a2a3
  so the orchestrator thread reads a promoted level when caching
  rt->orchestrator.l2_perf_level.
- Align l2_perf_aicpu_record_phase to a2a3 byte-for-byte: remove the
  end-of-function wmb() and the 3 dropped-path wmbs (all introduced
  by [hw-native-sys#777](hw-native-sys#777), none present in a2a3), and unify the accounting comment
  + brace style. Measured ~1.1 ms reduction in L4 orch_cost on
  paged_attention_unroll Case1.
- Align l2_perf_aicpu_complete_record with a2a3: add thread_idx
  parameter (routed from both host_build_graph and tensormap_and_ringbuffer
  callers), introduce an AICPU-private s_perf_records_buffers[] cache as
  the records-buffer SoT, rename switch_buffer -> switch_records_buffer
  and rotate after the write so the just-committed record is preserved,
  and surface ring/task_id mismatch as a dedicated LOG_ERROR
  (completion-before-dispatch invariant violation) separate from
  capacity drops. init / flush_buffers maintain s_perf_records_buffers[]
  in lockstep with state->current_buf_ptr so flush deterministically
  halts subsequent commits.

Update docs (l2-swimlane-profiling.md, profiling_levels.md,
testing.md) to drop the "a5 is boolean-only" caveat and document the
unified integer interface across a2a3 and a5.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants