From 6a5747ad4ea3b55509c44ca04b1e5485b1555e1b Mon Sep 17 00:00:00 2001 From: Reckless Apotheosis <125509978+recklessnode@users.noreply.github.com> Date: Wed, 10 Jun 2026 10:46:22 -0700 Subject: [PATCH] docs: add BZM2 port engineering notes under docs/bzm2/ Six notes from the BZM2 Mujina port (architecture, UART debug CLI guide, runtime control strategy, tuning planner, roadmap, opcode grounding), relocated from the bzm2-hwref hardware-documentation repo where they had been misfiled. Legacy-tree file paths scrubbed from the grounding note. Co-Authored-By: Claude Opus 4.8 --- docs/bzm2/blockscale-reference-roadmap.md | 332 ++++++++++++++++ docs/bzm2/bzm2-control-strategy.md | 303 +++++++++++++++ docs/bzm2/bzm2-opcode-grounding.md | 80 ++++ docs/bzm2/bzm2-pnp.md | 128 ++++++ docs/bzm2/bzm2-port.md | 371 ++++++++++++++++++ docs/bzm2/bzm2-uart-debug.md | 449 ++++++++++++++++++++++ 6 files changed, 1663 insertions(+) create mode 100644 docs/bzm2/blockscale-reference-roadmap.md create mode 100644 docs/bzm2/bzm2-control-strategy.md create mode 100644 docs/bzm2/bzm2-opcode-grounding.md create mode 100644 docs/bzm2/bzm2-pnp.md create mode 100644 docs/bzm2/bzm2-port.md create mode 100644 docs/bzm2/bzm2-uart-debug.md diff --git a/docs/bzm2/blockscale-reference-roadmap.md b/docs/bzm2/blockscale-reference-roadmap.md new file mode 100644 index 00000000..a7749971 --- /dev/null +++ b/docs/bzm2/blockscale-reference-roadmap.md @@ -0,0 +1,332 @@ +# Blockscale / BZM2 Reference Implementation Roadmap + +## Goal + +Close the remaining gap between: + +- a strong ASIC-facing Rust port with solid debug tooling + +and + +- a comprehensive, reusable reference implementation for custom Blockscale / + BZM2 hardware. + +This roadmap is ordered by dependency and practical value. + +## Scope + +In scope: + +- generic ASIC bring-up +- generic chain discovery +- reusable domain-aware power and tuning control +- board/API diagnostics +- runtime retune + +Out of scope for this plan: + +- vendor reference-board reproduction +- carrier-specific MCU protocols unless a target board actually needs them +- Gen1 telemetry completion +- speculative JTAG implementation not grounded in concrete protocol evidence + +## Current Gap Summary + +The current repo already has: + +- UART opcode support +- TDM parsing +- mining dispatch and result handling +- PLL and DLL diagnostics +- DTS/VS telemetry and query tooling +- startup tuning planning and saved operating-point replay +- a strong silicon-validation CLI + +The biggest missing pieces are: + +1. runtime engine/topology discovery instead of fixed assumptions +2. closed-loop calibration and retune +3. board/API diagnostics parity with the CLI + +## Phase 1: Discoverable Bring-Up + +Objective: + +- eliminate the assumption that ASIC count and identity are fully preconfigured + +Deliverables: + +1. Add low-level UART helpers for: + - writing `ASIC_ID` + - enumerating a chain starting from default `0xFA` + - verifying assigned IDs with `NOOP` +2. Add debug CLI support for: + - chain enumeration + - ID assignment validation +3. Add optional board startup enumeration mode so `Bzm2Board` can populate bus + layout from hardware rather than only from `MUJINA_BZM2_ASICS_PER_BUS` + +Status: + +- completed: low-level default-`ASIC_ID` enumeration helpers +- completed: `enumerate-chain` CLI support +- completed: opt-in `Bzm2Board` startup enumeration with fallback to + configured topology when no default-id ASICs are present +- next: Phase 2, applied rail and reset control + +Exit criteria: + +- a powered chain can be discovered from software with no hard-coded ASIC count +- the discovered count can seed board topology and saved operating-point + compatibility checks + +## Phase 2: Applied Rail And Reset Control + +Objective: + +- move the existing control abstractions from library-only status into real + board startup and shutdown flows + +Deliverables: + +1. Wire `Bzm2BringupPlan` into `Bzm2Board` +2. Add a concrete board-facing rail bundle abstraction: + - one or more rails + - optional reset line + - optional rail telemetry +3. Apply safe startup and shutdown sequencing through the board runtime +4. Expose rail telemetry into board state where available + +Status: + +- completed: `Bzm2BringupPlan` is now wired into `Bzm2Board` startup and + shutdown through generic file-backed rail and reset adapters +- completed: optional file-backed rail telemetry now flows into `BoardState` +- next: map planned domain voltages onto those startup/shutdown hooks + +Exit criteria: + +- board startup can perform reset and rail sequencing without external manual + steps +- board shutdown returns the hardware to a safe state + +## Phase 3: Domain Voltage Application + +Objective: + +- make the tuning planner’s voltage-domain outputs real rather than advisory + +Deliverables: + +1. Map planned domain voltages onto configured rails +2. Apply coarse domain voltages before clock ramp +3. Use rail telemetry and ASIC `DTS_VS` readings to verify applied state +4. Persist replay metadata that distinguishes: + - clock-only replay + - full voltage-plus-clock replay + +Exit criteria: + +- `Bzm2Board` can apply multi-domain operating points, not just PLL maps + +Status: + +- completed: planner-generated per-domain voltages are now mapped onto the + configured rail-control path before PLL ramp +- completed: saved operating-point replay now reapplies persisted per-domain + voltages before clock replay +- completed: live calibration persists per-domain rail targets for restart + replay +- next: Phase 4, topology and defect discovery + +## Phase 4: Topology And Defect Discovery + +Objective: + +- stop assuming the default logical engine map is always the real map + +Deliverables: + +1. Add engine/topology probing helpers +2. Detect unavailable or disabled engines per ASIC +3. Feed the discovered engine map into: + - work dispatch + - validation helpers + - tuning calculations + +Exit criteria: + +- systems with missing or disabled engines do not need a code rebuild or static + exclusion map edit + +Status: + +- completed: TDM-sync engine probe helpers now detect physical engine presence + by reading `ENGINE_REG_END_NONCE`, matching the historical C detection path +- completed: the debug CLI now supports: + - `engine-probe` + - `discover-engine-map` +- completed: discovered per-ASIC engine maps can now be pushed into live + `BoardState.asics` through: + - `Bzm2Board` command handling + - the live BZM2 thread actor + - `POST /api/v0/boards/{name}/bzm2/discover-engines` +- completed: successful discovery scans now update the live BZM2 runtime engine + layout used by: + - work dispatch fanout + - result reconstruction + - share validation helpers +- completed: calibration input now consumes active-engine counts and missing + coordinates through: + - live pre-calibration engine discovery when enabled + - saved operating-point topology replay + - default-map fallback when no topology data exists +- completed: saved operating-point reuse and planned hashrate estimation now + normalize against real engine capacity instead of assuming every ASIC has the + default full map +- next: Phase 5, closed-loop calibration and retune + +## Phase 5: Closed-Loop Calibration And Retune + +Objective: + +- turn the startup planner into a true operating-point controller + +Deliverables: + +1. Measure and store real: + - pass rate + - throughput + - per-PLL behavior + - per-domain power +2. Feed those measurements back into the tuning planner +3. Add runtime retune triggers for: + - throughput regression + - thermal drift + - persistent voltage imbalance +4. Revalidate or invalidate saved operating points automatically + +Exit criteria: + +- tuning decisions are based on measured runtime behavior, not just startup + heuristics and persisted estimates + +Status: + +- completed: live BZM2 threads now maintain work-based runtime throughput + estimators for: + - whole-thread throughput + - per-ASIC throughput + - per-PLL throughput using the documented row 0-9 / row 10-19 stack split +- completed: `Bzm2Board` now samples and stores runtime tuning measurements + into live board state and an internal cache, including: + - board throughput + - per-ASIC throughput + - per-ASIC average pass rate + - per-PLL pass rate and throughput + - per-domain measured voltage and power +- completed: the board runtime now feeds those live measurements back into the + existing tuning planner and publishes the current planner decision through + board state, including: + - reuse-saved-operating-point decision + - needs-retune decision + - desired voltage / clock / accept-ratio targets + - planner notes +- completed: runtime retune triggers are now promoted only after configurable + persistence across monitor polls for: + - throughput regression + - thermal drift + - persistent voltage imbalance +- completed: saved operating point profiles now carry runtime validation state + and are automatically: + - marked `validated` after clean runtime sampling + - marked `invalidated` when persistent retune triggers fire + - excluded from direct replay and planner seeding on later restarts once + invalidated +- next: Phase 6, diagnostics and API parity + +## Phase 6: Diagnostics And API Parity + +Objective: + +- expose the most useful silicon-validation operations without requiring the + standalone CLI + +Deliverables: + +1. Board/API commands for: + - `NOOP` + - loopback + - register read/write + - clock report + - chain enumeration summary +2. Board-state visibility for: + - discovered ASIC count + - discovered engine count / disabled-engine map + - saved operating-point replay path + - current calibration and safety status + +Status: + +- completed: board/API parity now covers live BZM2 thread-routed commands for: + - `NOOP` + - loopback + - register read/write +- completed: those diagnostics are exposed through HTTP endpoints that preserve + UART ownership by routing through the live BZM2 thread actor +- completed: the board/API surface now exposes `clock-report` parity through + the same live thread actor, so operators can inspect PLL/DLL lock state and + clock-control registers without dropping to the standalone CLI +- completed: the board/API surface now exposes a chain-summary view with: + - current per-bus serial path + - global ASIC ranges + - total discovered/configured ASIC count + - the startup path that produced the active operating point + - saved operating point validation state +- completed: Phase 6 exit criteria are now met for the planned board/API + diagnostics slice +- next: + - surface more of the same diagnostics through board state where useful + - only add broader manufacturing parity if there is a clear operator need + +Exit criteria: + +- operators can perform high-value diagnostics through the board/API surface + without dropping to raw serial tooling + +## Phase 7: JTAG, Only If Grounded + +Objective: + +- add JTAG only if enough packet-level evidence exists to do it correctly + +Deliverables: + +1. protocol-evidence review +2. minimal IR/DR helpers only if grounded +3. debug-only tooling for validated use cases + +Exit criteria: + +- no guessed JTAG semantics enter the codebase + +## Immediate Execution Order + +The next concrete work items should be: + +1. implement generic chain enumeration and `ASIC_ID` assignment helpers +2. add a debug CLI command that enumerates a live chain +3. add optional board startup auto-enumeration using that helper +4. then wire generic rail/reset sequencing into `Bzm2Board` + +## Current Execution Status + +Started: + +- Phase 1 step 1 and step 2 + +Reason: + +- enumeration removes a major assumption from the current board runtime +- it is ASIC-generic +- it is directly grounded in documented and legacy UART behavior diff --git a/docs/bzm2/bzm2-control-strategy.md b/docs/bzm2/bzm2-control-strategy.md new file mode 100644 index 00000000..f14f50da --- /dev/null +++ b/docs/bzm2/bzm2-control-strategy.md @@ -0,0 +1,303 @@ +# BZM2 Runtime Control Strategy + +## Purpose + +This note captures the recommended runtime control strategy for the BZM2 +implementation in Mujina. + +The goal is to move from: + +- startup calibration +- saved operating-point replay +- runtime measurement and retune detection + +to: + +- active runtime operating-point control +- user-selectable control objectives +- safe rollback to the last validated operating point + +## Current State + +The current implementation already has: + +- startup calibration planning +- per-domain voltage application during startup +- per-ASIC / per-PLL clock application during startup +- saved operating-point replay +- runtime measurement collection +- runtime retune detection and state reporting + +The current implementation does not yet have: + +- a runtime retune executor +- a closed-loop voltage / frequency controller +- user-selectable runtime control modes + +## Recommended Control Modes + +### 1. Target Temperature + +The user specifies a desired ASIC operating temperature in Celsius. + +The controller should: + +- use domain voltage as the primary control variable +- reduce voltage first when thermal headroom shrinks +- reduce clock only after voltage reaches a floor or thermal recovery is too slow +- interpret the user target as a temperature band, not a single exact switching point +- apply hysteresis so the controller does not chatter between two adjacent operating points + +This is the best first control mode because: + +- the thermal signal is relatively smooth +- the loop is easier to stabilize than hashrate tracking +- the necessary telemetry is already available + +### 2. Target Power + +The user specifies a board or domain power limit. + +The controller should: + +- use domain voltage as the primary control variable +- reduce clock only if voltage reaches its minimum safe point and power is still too high + +This is a strong second control mode because: + +- rail power telemetry already exists +- the objective is operationally useful +- the loop is simpler than pure hashrate hold + +### 3. Target Hashrate + +The user specifies a desired hashrate. + +The controller should: + +- raise or lower voltage within safe limits first +- use clock as a secondary control variable +- apply stronger smoothing and hysteresis than the thermal/power modes + +This should come after the first two modes because the signal is noisier and +depends on share-derived runtime estimates. + +## Control Architecture + +### Safety Layer + +The safety layer always overrides any target objective. + +Hard limits include: + +- maximum ASIC temperature +- maximum board / domain power +- minimum and maximum voltage +- PLL lock failure +- DTS/VS fault state +- missing or stale telemetry + +If any hard limit is violated: + +- move immediately to a safer operating point +- or fall back to the last validated operating point +- or idle / shut down if no safe state is available + +### Operating-Point Controller + +The operating-point controller chooses how to move the hardware toward the user +objective. + +Recommended priority: + +1. adjust voltage in small bounded domain-local steps +2. wait for electrical and thermal settling +3. re-evaluate +4. adjust clock only if voltage is insufficient or constrained + +This should be done: + +- per domain first +- per PLL second when split-stack behavior is enabled + +### Stability Layer + +The stability layer prevents oscillation. + +It should include: + +- persistence thresholds +- hysteresis bands +- moving averages / EWMA smoothing +- minimum dwell time between adjustments + +For temperature management specifically: + +- a target such as `72 C` should be treated as an operating band rather than a + literal single-value threshold +- for example, a controller may hold a nominal band around the target and only + step downward once the upper edge is exceeded for a sustained period +- likewise, it should only step upward again after the lower edge has been + crossed and the cooldown/recovery period has held long enough + +That prevents: + +- rapid toggling between two neighboring voltage or clock setpoints +- unnecessary PLL or rail churn +- control instability caused by normal telemetry noise or brief share-rate + variation + +## Why Not Pure PID Everywhere + +Mining hardware is not a perfectly smooth control system. + +Practical issues: + +- hashrate measurements are noisy +- share-derived throughput is bursty +- temperature reacts slowly +- clock changes are coarse +- voltage changes need settle time + +Recommended approach: + +- PID-like control for temperature +- bounded step controller for power +- stepwise optimizer with smoothing for hashrate + +For the temperature controller, the “PID-like” part should be wrapped in: + +- explicit hysteresis bands +- minimum time-at-setting requirements +- range-based targets instead of exact-value lock points + +## Recommended Actuation Order + +### Voltage First + +Use voltage as the primary runtime control variable because it directly affects: + +- thermal output +- efficiency +- stability margin + +Small voltage steps can often recover a target without a disruptive frequency +change. + +### Clock Second + +Use clock as the secondary control variable: + +- when voltage is already at a safe minimum or maximum +- when thermal or power error remains after voltage settling +- when the user target cannot be met by voltage adjustment alone + +## Runtime Metrics To Use + +The runtime controller should consume: + +- board throughput +- per-ASIC throughput +- per-PLL throughput where available +- ASIC temperature +- board / regulator temperature +- per-domain voltage +- per-domain power +- scheduler share counts +- current saved operating-point status + +Useful derived metrics: + +- watts per terahash +- joules per terahash +- temperature margin to limit +- voltage error against planned target +- normalized throughput per active engine + +## Required New Components + +### 1. Runtime Retune Executor + +This executor should: + +- consume retune requests +- build a new operating-point plan +- apply voltage and clock changes safely +- verify the result +- mark the result validated, pending, or failed + +### 2. Generic Diagnostics Surface + +The runtime controller should not own raw device access directly. + +Instead, it should build on a generic diagnostics / control surface that can: + +- read telemetry +- query current operating point +- apply domain voltage +- apply per-PLL or per-ASIC clock changes +- verify lock / health status + +### 3. Rollback Path + +The controller must be able to: + +- revert to the last validated operating point +- preserve that operating point until a replacement is proven good + +This is especially important because runtime retune is inherently riskier than +startup calibration. + +## Suggested Configuration Surface + +Potential user-facing control settings: + +- `control_mode` +- `target_hashrate_ths` +- `target_temp_c` +- `target_power_w` +- `voltage_step_mv` +- `clock_step_mhz` +- `settle_delay_ms` +- `retune_persistence_polls` +- `retune_hysteresis_temp_c` +- `retune_hysteresis_power_w` + +The exact configuration surface does not need to be finalized before the +executor exists, but the design should assume these concepts. + +## Recommended Implementation Order + +1. add the runtime retune executor framework +2. implement `target_temperature` with domain-voltage control +3. implement rollback to last validated operating point +4. implement `target_power` +5. implement `target_hashrate` +6. add per-PLL refinements for split-stack boards + +## Best First Mode + +The best first runtime control mode is: + +- `target_temperature` + +Reason: + +- easiest to stabilize +- directly useful to operators +- uses telemetry already present in the BZM2 implementation +- safer than chasing exact hashrate with noisy share-derived measurements + +## Summary + +The runtime control design should evolve from advisory tuning state into an +active control system that: + +- respects safety limits first +- adjusts domain voltage before clock +- uses smoothing and hysteresis +- preserves the last validated operating point until a replacement is proven +- supports user-selectable operating objectives + +The main implementation gap is no longer planning logic. It is the executor +that safely applies and verifies runtime operating-point changes. diff --git a/docs/bzm2/bzm2-opcode-grounding.md b/docs/bzm2/bzm2-opcode-grounding.md new file mode 100644 index 00000000..e1177272 --- /dev/null +++ b/docs/bzm2/bzm2-opcode-grounding.md @@ -0,0 +1,80 @@ +# BZM2 Opcode And JTAG Grounding + +## Scope + +This note captures only behavior that is grounded in material available in this workspace: + +- top-level PDFs in `../docs` +- legacy UART implementation in the legacy bzmd `uart.h` and the legacy bzmd `uart.c` +- legacy exercised behavior in the legacy bzmd `test.c` + +Anything not evidenced there is intentionally excluded from the Mujina port. + +## What The PDFs Proved + +The top-level PDFs are present: + +- the vendor JTAG usage guide +- the vendor opcode explanation guide + +From this shell, the PDFs are not extractable into reliable body text. The only recoverable textual evidence was: + +- JTAG PDF bookmark text: `PLL0/PLL1 debug signals` +- opcode PDF outline text: `1 Document Revision History` + +That is not enough to justify implementing a JTAG protocol layer or inventing undocumented opcode semantics. + +## What The Legacy Source Proved + +The legacy `bzmd` source gives a concrete UART wire contract for these opcodes: + +- `WRITEJOB` +- `READRESULT` +- `WRITEREG` +- `READREG` +- `MULTICAST_WRITE` +- `DTS_VS` +- `LOOPBACK` +- `NOOP` + +Grounded request/response behavior from the legacy bzmd `uart.c`: + +- `WRITEREG`: request is `len(2 LE) + header(4 BE) + count_minus_one + payload` +- `MULTICAST_WRITE`: same framing as `WRITEREG`, but opcode `0x4` +- `READREG`: request is fixed-length `8` byte frame with terminal target byte; direct response is `asic + opcode + payload` +- `READRESULT`: in TDM mode, result frame is `asic + opcode + 8-byte payload` +- `NOOP`: request is a 4-byte frame; response payload is 3 bytes +- `LOOPBACK`: request is `len + header + count_minus_one + payload`; response echoes `asic + opcode + payload` +- `DTS_VS`: in TDM mode, payload is 4 bytes for gen1 and 8 bytes for gen2 + +Grounded concurrency and parser behavior from the legacy bzmd `uart.h`, the legacy bzmd `uart.c`, and the legacy bzmd `test.c`: + +- TDM parsing is byte-stream oriented and must resynchronize after unknown prefixes +- TDM `READREG` response size is caller-driven and tracked per ASIC +- one outstanding TDM register read per ASIC is the supported model +- one outstanding TDM noop per ASIC is the supported model +- broadcast register writes use `WRITEREG` with ASIC `0xFF`, not a separate broadcast opcode +- broadcast TDM register reads are layered on top of `READREG`, not a distinct opcode + +## What Mujina Now Grounds + +Current Mujina BZM2 support in [protocol.rs](../../mujina-miner/src/asic/bzm2/protocol.rs) is now explicitly locked to the legacy-tested UART behavior for: + +- `WRITEREG`, `READREG`, `WRITEJOB`, `MULTICAST_WRITE`, `READRESULT`, `NOOP`, `LOOPBACK`, `DTS_VS` +- gen1 and gen2 DTS/VS payload decoding +- partial-frame buffering and resynchronization after unknown byte prefixes +- legacy wire-format invariants for register, noop, and loopback command encoders + +## Deliberate Exclusions + +Not implemented from the docs side: + +- JTAG command transport +- JTAG IR/DR scan helpers +- PLL debug readout sequences +- any opcode semantics that cannot be traced to shipped UART code or tests + +Reason: + +- the available source in this workspace proves the UART mining/control path +- the available PDF extraction on this machine does not provide enough packet-level JTAG detail to implement anything defensible \ No newline at end of file diff --git a/docs/bzm2/bzm2-pnp.md b/docs/bzm2/bzm2-pnp.md new file mode 100644 index 00000000..18a76c52 --- /dev/null +++ b/docs/bzm2/bzm2-pnp.md @@ -0,0 +1,128 @@ +# BZM2 PnP Calibration In Mujina + +This note captures the current BZM2 PnP state in Mujina, what the legacy `bzmd` implementation did, and what is now implemented in the Rust port. + +## Current Gap + +Before this change, Mujina's BZM2 support had: + +- UART work dispatch +- result parsing +- thermal and power safety shutdowns +- UART register access +- PLL and DLL control + +What it did not have was a native Mujina tuning planner for BZM2: + +- operating-class and performance-mode target selection +- parameter sweep generation +- initial voltage and frequency selection from site temperature +- saved operating point reuse checks +- retune decisions when measured throughput regresses +- domain-aware planning for hardware with multiple voltage domains +- per-ASIC or per-stack frequency fine-tuning around a target pass-rate window + +## Legacy `pnp.c` Behavior + +The original C implementation mixed: + +- calibration search policy +- board and PSU policy +- persisted board calibration profiles +- per-ASIC telemetry accumulation +- per-engine pass-rate accounting +- platform-specific data collection and file I/O + +The reusable algorithmic parts are: + +- derive voltage, clock, and acceptance targets from operating class and performance mode +- derive initial voltage and clock from site thermal conditions +- broadcast a starting frequency +- sweep upward while respecting power and thermal guard rails +- tune back down on individual ASICs or stacks when pass rate falls outside the target window +- invalidate saved operating point when throughput regresses materially + +## Mujina Ported Behavior + +The new Rust module at +`mujina-miner/src/asic/bzm2/pnp.rs` +implements the reusable planner without pulling board-MCU or PSU glue into the ASIC layer. + +Implemented: + +- operating-class target tables for: + - generic + - EarlyValidation + - ProductionValidation + - StackTunedA + - StackTunedB + - ExtendedHeadroom + - ExtendedHeadroomB +- search-space generation corresponding to the historical C sweep helper +- site-temperature-aware initial voltage and clock planning corresponding to the historical C startup helper +- saved operating point reuse vs. full retune decisions +- domain-aware voltage planning using explicit voltage-domain offsets and guards +- per-domain frequency planning using aggregated pass-rate, thermal, and power data +- per-ASIC fine-tuning with optional per-stack / per-PLL behavior + +## Efficiency Model + +The planner is structured to scale cleanly from a single ASIC to large chains: + +- one pass to aggregate domain-level metrics +- one pass to emit per-domain plans +- one pass to emit per-ASIC adjustments + +That keeps the planning work effectively linear in ASIC count for normal use. + +For larger systems with multiple voltage domains, the planner prefers: + +- domain-level voltage decisions first +- domain-average frequency targets next +- per-ASIC or per-PLL corrections only where pass-rate or thermal data requires it + +That is materially more scalable than treating a 100-ASIC machine as 100 independent full-search problems. + +## Scope Boundary + +The planner is now wired into `Bzm2Board` startup so Mujina can: + +- execute a live pre-thread calibration phase +- persist applied calibration results as a saved operating point profile +- replay a compatible saved operating point profile directly on restart before falling back to retune +- collect live runtime tuning measurements during mining for: + - board throughput + - per-ASIC throughput + - per-ASIC average pass rate + - per-PLL throughput and pass rate + - per-domain measured rail voltage and power +- normalize saved-throughput comparisons and planned board hashrate against + actual active-engine capacity instead of assuming every ASIC still has the + default full map +- run the same planner against live runtime measurements during mining so the + board can continuously evaluate whether the current operating point is still + valid +- automatically promote saved operating point state from `pending` to + `validated` after clean runtime sampling +- automatically invalidate saved operating point profiles when persistent + runtime retune triggers fire, so restart replay will not reuse a known-bad + operating point + +Engine-capacity inputs now come from, in order: + +- live pre-calibration engine discovery when enabled +- saved per-ASIC topology embedded in the saved operating point profile +- default BZM2 hole-map fallback when no better topology data is available + +That means tuning decisions can now distinguish between: + +- a slow ASIC that still has full engine capacity +- an ASIC that is throughput-limited because it has permanently missing engines + +What still remains outside the ASIC planner layer: + +- board-specific PSU ramp policy +- reimplementation of the legacy CSV/database layer + +Those pieces still belong above the ASIC planner, in board or daemon integration layers. + diff --git a/docs/bzm2/bzm2-port.md b/docs/bzm2/bzm2-port.md new file mode 100644 index 00000000..60c49aac --- /dev/null +++ b/docs/bzm2/bzm2-port.md @@ -0,0 +1,371 @@ +# BZM2 Mujina Port + +## Architecture + +This port keeps BZM2 support inside Mujina rather than reviving the original split `cgminer` + `bzmd` process model. + +The legacy split looked like this: + +- `cgminer` handled scheduling, pool interaction, and IPC to `bzmd` +- `bzmd` owned UART transport, job fanout, result validation, and board-management glue + +In Mujina, those responsibilities map cleanly onto existing abstractions: + +- `Daemon` injects a virtual `bzm2` board when configured +- `Backplane` instantiates the virtual board through `inventory` +- `board::bzm2::Bzm2Board` opens serial transports and creates hash threads +- `asic::bzm2::Bzm2Thread` performs direct UART job dispatch, telemetry parsing, and share validation +- `asic::bzm2::control` provides reusable GPIO-reset and PMBus/I2C rail sequencing primitives + +A standalone Rust daemon is therefore not required for the mining path. + +## Bring-Up And Shutdown + +`Bzm2Board` now supports optional board-level power and reset sequencing through +the existing `Bzm2BringupPlan`. + +The current generic integration path uses file-backed adapters: + +- rail setpoint files for coarse voltage application +- optional rail enable files for regulator enable or precharge control +- an optional reset file for ASIC reset assertion and release + +This keeps the implementation generic across custom Linux-based carriers without +hard-coding one vendor board layout or management MCU protocol. + +## Implemented Behavior + +The BZM2 Mujina thread now reimplements the core legacy data path and the generally reusable portions of the control path: + +- 20 x 12 logical engine grid with the four excluded engines from legacy code +- enhanced-mode 4-midstate dispatch per logical engine +- version-rolling micro-jobs in slots `0, 2, 4, 8` +- UART register writes for target bits, leading-zero threshold, and timestamp count +- TDM result parsing with sequence parity matching +- nonce correction via enhanced-mode nonce gap +- in-thread Bitcoin header reconstruction and share validation before scheduler submission +- UART opcode coverage for: + - `WRITEJOB` + - `WRITEREG` + - `READREG` + - `MULTICAST_WRITE` + - `READRESULT` + - `NOOP` + - `LOOPBACK` + - `DTS_VS` +- DTS/VS generation 1 and generation 2 frame decoding +- live DTS/VS gen2 hardware-fault handling that shuts down the hash thread on thermal or voltage fault indications +- reusable GPIO reset-line control through `AsicEnable` +- reusable TPS546 PMBus rail control through `VoltageRegulator` +- reusable multi-rail bring-up and shutdown sequencing for single-rail, small-stack, and larger multi-stack designs +- UART-register-based PLL diagnostic/control flow for divider programming, enable/disable, lock polling, and readback +- UART-register-based DLL diagnostic/control flow for duty-cycle programming, enable/disable, lock polling, and fincon validation +- developer-facing UART debug CLI documented in [bzm2-uart-debug.md](bzm2-uart-debug.md) with unicast, multicast, and broadcast examples +- domain-aware BZM2 tuning planner documented in [bzm2-pnp.md](bzm2-pnp.md) for operating-class and performance-mode target selection, search-space generation, and per-domain plus per-ASIC tuning + +## Configuration + +Enable BZM2 by setting `MUJINA_BZM2_SERIAL` to one or more comma-separated serial device paths. + +Supported environment variables: + +- `MUJINA_BZM2_SERIAL`: required, comma-separated serial device paths +- `MUJINA_BZM2_SERIAL_PATHS`: alternate name for the same setting +- `MUJINA_BZM2_BAUD`: UART baud rate, default `5000000` +- `MUJINA_BZM2_TIMESTAMP_COUNT`: default `60` +- `MUJINA_BZM2_NONCE_GAP`: default `0x28` +- `MUJINA_BZM2_DISPATCH_MS`: redispatch interval in milliseconds, default `500` +- `MUJINA_BZM2_HASHRATE_THS`: nominal per-thread hashrate estimate, default `40` +- `MUJINA_BZM2_DTS_VS_GEN`: DTS/VS payload generation, `1` or `2`, default `2` +- `MUJINA_BZM2_ENUMERATE_CHAIN`: enable startup chain enumeration from the + documented default `ASIC_ID` +- `MUJINA_BZM2_AUTO_ENUMERATE`: alternate name for the same setting +- `MUJINA_BZM2_ENUM_START_ID`: first assigned runtime `ASIC_ID`, default `0` +- `MUJINA_BZM2_ENUM_MAX_ASICS_PER_BUS`: comma-separated per-bus enumeration + ceilings, default `100` per bus unless calibration topology already provides + a larger configured count +- `MUJINA_BZM2_ENABLE_BRINGUP`: enable startup and shutdown rail/reset + sequencing +- `MUJINA_BZM2_BRINGUP_ENABLE`: alternate name for the same setting +- `MUJINA_BZM2_RAIL_SET_PATHS`: comma-separated rail-control file paths +- `MUJINA_BZM2_RAIL_TARGET_VOLTS`: comma-separated target rail voltages for the + bring-up plan +- `MUJINA_BZM2_RAIL_WRITE_SCALES`: optional comma-separated scale factors used + when converting volts into the raw file value, for example `1000` for mV or + `1000000` for uV +- `MUJINA_BZM2_DOMAIN_RAIL_INDICES`: optional comma-separated mapping from + planner domain id to configured rail index; defaults to one-to-one + `domain_id -> rail_index` when omitted +- `MUJINA_BZM2_RAIL_ENABLE_PATHS`: optional comma-separated enable/control file + paths paired with the rail list +- `MUJINA_BZM2_RAIL_ENABLE_VALUES`: optional comma-separated values written to + the rail enable paths during rail initialization +- `MUJINA_BZM2_RAIL_VIN_PATHS`: optional comma-separated rail input-voltage + sensor files +- `MUJINA_BZM2_RAIL_VIN_SCALES`: optional scale factors for the rail input + voltage files +- `MUJINA_BZM2_RAIL_VOUT_PATHS`: optional comma-separated rail output-voltage + sensor files +- `MUJINA_BZM2_RAIL_VOUT_SCALES`: optional scale factors for the rail output + voltage files +- `MUJINA_BZM2_RAIL_CURRENT_PATHS`: optional comma-separated rail current sensor + files +- `MUJINA_BZM2_RAIL_CURRENT_SCALES`: optional scale factors for the rail current + files +- `MUJINA_BZM2_RAIL_POWER_PATHS`: optional comma-separated rail power sensor + files +- `MUJINA_BZM2_RAIL_POWER_SCALES`: optional scale factors for the rail power + files +- `MUJINA_BZM2_RAIL_TEMP_PATHS`: optional comma-separated rail regulator + temperature sensor files +- `MUJINA_BZM2_RAIL_TEMP_SCALES`: optional scale factors for the rail + regulator temperature files +- `MUJINA_BZM2_RESET_PATH`: optional reset-control file path +- `MUJINA_BZM2_RESET_ACTIVE_LOW`: whether the reset path is active-low, default + `true` +- `MUJINA_BZM2_BRINGUP_PRE_POWER_MS`: delay before rail initialization, default + `10` +- `MUJINA_BZM2_BRINGUP_POST_POWER_MS`: delay after the configured rail steps, + default `25` +- `MUJINA_BZM2_BRINGUP_RELEASE_RESET_MS`: delay after reset release, default + `25` + +Startup enumeration notes: + +- this mode is intended for fresh chains where ASICs still answer on the + default `ASIC_ID` +- enumeration uses a bounded `NOOP` probe so the chain walk terminates cleanly + at the end of the bus +- if no default-id ASIC responds on startup, Mujina falls back to the configured + `MUJINA_BZM2_ASICS_PER_BUS` topology so warm-restart cases do not collapse to + zero ASICs + +Bring-up notes: + +- if bring-up is enabled, `Bzm2Board` applies the configured rail/reset + sequence before chain discovery, calibration, and hash-thread creation +- when the tuning planner produces per-domain voltage targets, `Bzm2Board` + now applies them onto the configured rail-control path before the PLL ramp + rather than treating them as advisory only +- saved operating-point replay now reapplies persisted per-domain voltages + before clock replay when the profile contains them +- if multiple domains are mapped onto one rail, the runtime requires them to + agree on the same target voltage; conflicting targets fail loudly instead of + applying an ambiguous setpoint +- on board shutdown, the same plan is used in reverse order to assert reset and + drive the configured rails back to `0` +- the current implementation is still coarse-grained at the regulator layer: + it applies domain targets onto configured rails, but it does not yet perform + closed-loop voltage verification against live rail telemetry or runtime retune + +If the optional rail telemetry files are configured, the board monitor also +publishes them into normal board state using stable names: + +- `rail0-input`, `rail1-input`, ... for input-side voltage snapshots +- `rail0-output`, `rail1-output`, ... for output-side voltage/current/power +- `rail0-regulator`, `rail1-regulator`, ... for regulator temperatures + +## API Telemetry + +When Gen2 `DTS_VS` frames are present on the UART path, Mujina now surfaces ASIC-internal telemetry through the normal board API state: + +- `BoardState.temperatures` +- `BoardState.powers` + +The values are named per serial bus and per ASIC so they can coexist with host-side sensor files: + +- temperature: `ttyUSB0-asic-2-dts` +- voltage channels: `ttyUSB0-asic-2-vs-ch0`, `ttyUSB0-asic-2-vs-ch1`, `ttyUSB0-asic-2-vs-ch2` + +Example JSON fragment: + +```json +{ + "temperatures": [ + { "name": "ttyUSB0-asic-2-dts", "temperature_c": 64.5 } + ], + "powers": [ + { "name": "ttyUSB0-asic-2-vs-ch0", "voltage_v": 0.78, "current_a": null, "power_w": null }, + { "name": "ttyUSB0-asic-2-vs-ch1", "voltage_v": 0.79, "current_a": null, "power_w": null }, + { "name": "ttyUSB0-asic-2-vs-ch2", "voltage_v": 0.77, "current_a": null, "power_w": null } + ] +} +``` + +Notes: + +- these ASIC-originated entries are merged into board state and do not replace host-file telemetry +- Celsius and voltage scaling follow the legacy `bzmd` DTS/VS conversion formulas +- Gen1 currently exposes voltage through this path, but not a Celsius temperature reading + +## On-Demand ASIC Sensor Queries + +Mujina now supports explicit DTS/VS query operations in addition to passive frame reporting. + +This is useful when: + +- the miner is idle and no passive DTS/VS traffic is arriving +- one ASIC is misbehaving and needs targeted inspection +- developers want a direct sensor read without enabling a full TDM watch session + +Two access paths are implemented: + +- CLI: `mujina-bzm2-debug dts-vs-query` and `mujina-bzm2-debug dts-vs-scan` +- HTTP API: `POST /api/v0/boards/{name}/bzm2/dts-vs-query` + +The query path runs through the live BZM2 hash-thread actor so UART ownership remains correct. Queried frames are converted through the same telemetry code path used for passive DTS/VS reporting, so the returned values land in normal `BoardState` telemetry. + +Example HTTP request: + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/dts-vs-query \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2}' +``` + +See [bzm2-uart-debug.md](bzm2-uart-debug.md) for CLI usage examples and expected output shape. + +## On-Demand Engine Discovery + +Mujina also supports explicit per-ASIC engine-map discovery when the thread is +idle. + +This is useful when: + +- an ASIC is returning unstable shares and the default engine-hole assumption is + no longer trustworthy +- developers need to compare the live engine map against the historical default + BZM2 hole pattern +- operators want the discovered topology recorded in normal board API state + +The engine-discovery path runs through the live BZM2 hash-thread actor, just +like the DTS/VS query path, so UART ownership stays correct. Successful scans +update the live thread engine layout and `BoardState.asics` with: + +- `id` +- `thread_index` +- `serial_path` +- `discovered_engine_count` +- `missing_engines` + +After a successful idle-time scan, subsequent work dispatch and result +reconstruction use the discovered active-engine layout instead of the built-in +default four-hole map. + +HTTP API: + +- `POST /api/v0/boards/{name}/bzm2/discover-engines` + +Example HTTP request: + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/discover-engines \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2,"tdm_prediv_raw":15,"tdm_counter":16,"timeout_ms":150}' +``` + +The response returns the refreshed `BoardState`, including the updated +`asics` topology entry for the queried ASIC. + +## API Diagnostics + +The board/API surface now exposes a first live UART diagnostics slice through +the BZM2 thread actor: + +- `POST /api/v0/boards/{name}/bzm2/noop` +- `POST /api/v0/boards/{name}/bzm2/loopback` +- `POST /api/v0/boards/{name}/bzm2/register-read` +- `POST /api/v0/boards/{name}/bzm2/register-write` +- `POST /api/v0/boards/{name}/bzm2/clock-report` + +These commands intentionally route through the live board-owned thread instead +of opening a second serial handle. That keeps UART ownership correct and avoids +silent contention with the mining path. + +Current safety boundary: + +- the target thread must be idle +- DTS/VS streaming must be inactive on that thread + +If either condition is false, the command is rejected rather than racing active +mining traffic or background telemetry frames. + +`clock-report` returns the same PLL/DLL status surface already exposed by the +standalone Rust debug CLI: + +- PLL enable register +- PLL misc register +- PLL enabled/locked bits +- DLL control2/control5 values +- DLL `coarsecon` +- DLL `fincon` +- DLL freeze-valid, lock, and fincon-valid state + +## Chain Summary + +The board/API surface also exposes the current BZM2 chain layout without +opening a second serial handle: + +- `GET /api/v0/boards/{name}/bzm2/chain-summary` + +The response summarizes: + +- current UART bus count +- serial path per bus +- global ASIC start/count per bus +- total ASIC count across the board +- whether the active operating point came from saved replay or live calibration +- current saved operating point validation state + +That gives operators a stable summary view of the active chain layout even when +the underlying board was discovered by startup enumeration rather than static +configuration alone. + +## Design Boundary + +The legacy `bzmd` board-power path mixes three different concerns: + +- genuinely reusable sequencing concepts +- generic peripheral protocols like PMBus/I2C regulators and reset GPIOs +- highly board-specific MCU command sets, sysfs GPIO numbering, CAN PSU control, and platform wiring assumptions + +Only the first two belong in a generally applicable Mujina BZM2 implementation. + +Ported into Mujina: + +- generic reset assertion/deassertion +- generic ordered rail bring-up and shutdown +- generic PMBus/TPS546 voltage control and telemetry adapters +- ASIC-originated DTS/VS telemetry and fault handling + +Intentionally not ported verbatim: + +- Intel board MCU command protocol from `mcu.c` +- hard-coded board GPIO numbering and sysfs reset pulses from `util.c` / `daemon.c` +- platform CAN PSU control from `psu.c` +- board-specific fan and ambient-sensor plumbing that depends on the original platform layout + +Those pieces should only be added behind a concrete Mujina board implementation when the target hardware actually uses them. + +## Current Limits + +Still not implemented from the broader legacy stack: + +- JTAG workflows from the standalone platform documents +- JTAG-only PLL debug sequences that are not represented in the shipped UART code +- calibration and autotuning state machines +- full manufacturing and diagnostics RPC parity + - beyond the current live API surface for: + - `NOOP` + - loopback + - register read/write + - clock report + - chain summary +- any board-MCU protocol that is specific to one carrier or backplane design + +The top-level `docs` PDFs reference additional JTAG and opcode material, but this port currently implements the opcode surface that is evidenced in the legacy shipping UART path and not an inferred JTAG control plane. + + +See also: + +- [bzm2-opcode-grounding.md](bzm2-opcode-grounding.md) for the source-grounded opcode matrix and the current JTAG evidence boundary diff --git a/docs/bzm2/bzm2-uart-debug.md b/docs/bzm2/bzm2-uart-debug.md new file mode 100644 index 00000000..ff8696e3 --- /dev/null +++ b/docs/bzm2/bzm2-uart-debug.md @@ -0,0 +1,449 @@ +# BZM2 UART Debug Guide + +This guide documents the direct BZM2 UART and silicon-validation interface added to Mujina. + +## Intent + +The current CLI folds in the portable parts of the legacy silicon validation surface: + +- chain enumeration and `ASIC_ID` assignment from the default bus id +- ASIC discovery and liveness scans +- loopback data-path validation +- explicit TDM enable and disable control +- live TDM result, register, noop, and DTS/VS observation +- broadcast TDM register-read observation across many ASICs +- engine presence probing and full physical engine-map discovery +- engine-wide timestamp, target, and leading-zero programming +- deterministic grid-job exercisers for single-phase and back-to-back job dispatch +- existing PLL and DLL diagnostics and bring-up helpers + +What is deliberately not ported here: + +- legacy board-reset and board-count orchestration +- BCH-vector and nonce-golden-data regression harnesses +- opaque manufacturing hooks and carrier-specific glue + +## Routing Modes + +- unicast: one ASIC, one destination address +- broadcast: all ASICs on the UART bus via ASIC id `0xff` +- multicast: one ASIC, one engine-row group + +## Binary + +Use [bzm2-debug.rs](../mujina-miner/src/bin/bzm2-debug.rs) through Cargo: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- ... +``` + +## Legacy Test Mapping + +The most useful silicon validation tests map to the following commands: + +- `test_noop_all_asic` -> `noop-scan` +- `test_loopback` -> `loopback-scan` +- `test_effbst_tdm_selectable_leadingzeros` -> `engine-zeros-all` plus `job-grid-watch` +- `test_effbst_tdm_jobsubmit_with_writejobcmd` -> `job-grid` or `job-grid-watch` +- `test_effbst_tdm_continuous_b2b_jobs` -> `job-grid-2phase-watch` +- `uart_command_broadcast_readreg_tdm_async` flows -> `tdm-broadcast-read-watch` +- general TDM callback validation -> `tdm-watch` +- historical engine-detect flow -> `engine-probe` or `discover-engine-map` + +The old multicasted-EFFBST and auto-clock-gating tests depended on the legacy BCH vector library and runtime board harness. The new CLI does not claim golden nonce validation there; it provides deterministic packet generation and live observation so developers can still exercise the same ASIC paths when debugging hardware. + +## Direct UART Examples + +Read one ASIC-local register: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + uart-read /dev/ttyUSB0 2 notch 0x12 4 5000000 +``` + +Write one ASIC-local register: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + uart-write /dev/ttyUSB0 2 notch 0x12 01000000 5000000 +``` + +Run a NOOP sanity check across an ASIC range: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + noop-scan /dev/ttyUSB0 0 15 5000000 +``` + +Enumerate a powered chain from the default `ASIC_ID` (`0xFA`) and assign +incrementing ids starting at `0`: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + enumerate-chain /dev/ttyUSB0 32 0 5000000 +``` + +This is the first generic bring-up step for a fresh chain where every ASIC is +still on the default id. + +The chain walk now uses a bounded `NOOP` probe internally, so the command stops +cleanly when no additional default-id ASIC is present instead of hanging at the +end of the bus. + +The same discovery flow can be enabled during Mujina board startup with: + +- `MUJINA_BZM2_ENUMERATE_CHAIN=true` +- optional `MUJINA_BZM2_ENUM_START_ID` +- optional `MUJINA_BZM2_ENUM_MAX_ASICS_PER_BUS` + +Run deterministic loopback validation across an ASIC range: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + loopback-scan /dev/ttyUSB0 0 15 8 5000000 +``` + +Read one synchronous result frame from one ASIC: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + uart-read-result /dev/ttyUSB0 2 gen2 5000000 +``` + +## API Visibility + +Passive Gen2 `DTS_VS` traffic is now reflected in the board API as named ASIC telemetry. + +Expected sensor names: + +- temperature: `ttyUSB0-asic-2-dts` +- voltage: `ttyUSB0-asic-2-vs-ch0`, `ttyUSB0-asic-2-vs-ch1`, `ttyUSB0-asic-2-vs-ch2` + +This is board-state telemetry, not a separate debug-only channel. If the miner is already receiving `DTS_VS` frames during operation, these entries appear in the normal board JSON under: + +- `temperatures` +- `powers` + +Use `tdm-watch` when you need to correlate the raw UART frames with the API-visible values. + +## On-Demand DTS/VS Queries + +Passive telemetry is useful when the miner is already receiving `DTS_VS` traffic. When hardware is misbehaving, you often need an explicit query path that forces a fresh ASIC sensor read. + +The debug CLI now supports direct on-demand DTS/VS reads: + +Query one ASIC: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + dts-vs-query /dev/ttyUSB0 2 gen2 1500 5000000 +``` + +Scan an ASIC range and print each returned DTS/VS frame: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + dts-vs-scan /dev/ttyUSB0 0 15 gen2 1500 5000000 +``` + +These commands: + +- enable the DTS/VS path if it is not already configured +- wait for a matching DTS/VS frame from the requested ASIC +- print temperature and voltage information in engineering units +- preserve the normal passive telemetry path so the same readings can still appear in board state later + +The `dts-gen` argument should match the ASIC telemetry generation in use: + +- `gen1` +- `gen2` + +Typical output includes: + +- ASIC id +- thermal status and trip bits +- Celsius temperature when available from the generation-specific decode path +- voltage channels in volts + +## HTTP API Query Example + +Mujina also exposes the same operation through the board API for live boards: + +```text +POST /api/v0/boards/{name}/bzm2/dts-vs-query +``` + +Example: + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/dts-vs-query \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2}' +``` + +The response is the refreshed board JSON after the query completes. The relevant values appear in the normal board-state telemetry collections, for example: + +```json +{ + "temperatures": [ + { "name": "ttyUSB0-asic-2-dts", "temperature_c": 64.5 } + ], + "powers": [ + { "name": "ttyUSB0-asic-2-vs-ch0", "voltage_v": 0.78, "current_a": null, "power_w": null } + ] +} +``` + +Request fields: + +- `thread_index`: which BZM2 hash thread owns the target UART bus +- `asic`: ASIC id to query on that bus + +## HTTP API Diagnostics + +Mujina now exposes a first board/API parity slice for live UART diagnostics +without dropping to a separate serial tool. These operations route through the +live BZM2 thread actor, so they preserve UART ownership and operate against the +same board instance visible in the normal API. + +Available endpoints: + +- `POST /api/v0/boards/{name}/bzm2/noop` +- `POST /api/v0/boards/{name}/bzm2/loopback` +- `POST /api/v0/boards/{name}/bzm2/register-read` +- `POST /api/v0/boards/{name}/bzm2/register-write` +- `POST /api/v0/boards/{name}/bzm2/clock-report` + +Examples: + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/noop \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2}' +``` + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/loopback \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2,"payload_hex":"0102aabb"}' +``` + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/register-read \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2,"engine_address":4095,"offset":18,"count":4}' +``` + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/register-write \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2,"engine_address":4095,"offset":18,"value_hex":"deadbeef"}' +``` + +```bash +curl -X POST http://127.0.0.1:3000/api/v0/boards/bzm2-0/bzm2/clock-report \ + -H "Content-Type: application/json" \ + -d '{"thread_index":0,"asic":2}' +``` + +Representative responses: + +```json +{ "payload_hex": "425a32" } +``` + +```json +{ "payload_hex": "0102aabb" } +``` + +```json +{ "value_hex": "11223344" } +``` + +```json +{ "bytes_written": 4 } +``` + +Current safety boundary: + +- these live UART diagnostics require the target BZM2 thread to be idle +- they also require DTS/VS streaming to be inactive on that thread +- if those conditions are not met, the board command returns an error instead + of competing with active mining or background telemetry traffic + +## TDM Control and Observation + +The legacy `enable_tdm()` path is grounded in a local-register write to `LOCAL_REG_UART_TDM_CTL` (`0x07`) with control word: + +```text +(prediv_raw << 9) | (tdm_counter << 1) | enable_bit +``` + +Enable TDM explicitly: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + tdm-enable /dev/ttyUSB0 0x12 16 5000000 +``` + +Disable TDM explicitly: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + tdm-disable /dev/ttyUSB0 0x12 16 5000000 +``` + +Watch the mixed TDM stream for five seconds: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + tdm-watch /dev/ttyUSB0 gen2 5 5000000 +``` + +Broadcast a register read and collect returned TDM read frames from ASICs `0` through `15`: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + tdm-broadcast-read-watch /dev/ttyUSB0 gen2 0 15 notch 0x12 4 5000000 +``` + +Probe one physical engine coordinate using the historical TDM-sync +`ENGINE_REG_END_NONCE` detection rule: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + engine-probe /dev/ttyUSB0 2 0 0 0x0f 16 100 5000000 +``` + +Scan the full `20 x 12` physical grid and report the discovered hole map for +one ASIC: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + discover-engine-map /dev/ttyUSB0 2 0x0f 16 100 5000000 +``` + +Notes: + +- these commands intentionally scan all physical coordinates, not the legacy + default engine list +- discovery uses the source-grounded rule from the historical C path: + `ENGINE_REG_END_NONCE == 0xfffffffe` means the engine is present +- the CLI reports whether the discovered missing coordinates still match the + default four-hole BZM2 map or whether the ASIC diverges from that assumption + +## Engine-Wide Programming Helpers + +Set the target register across every engine row on one ASIC or the whole bus: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + engine-target-all /dev/ttyUSB0 2 0x1705ffff 5000000 +``` + +Set timestamp count across all engine rows: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + engine-timestamp-all /dev/ttyUSB0 2 60 5000000 +``` + +Program selectable leading zeros across all engine rows: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + engine-zeros-all /dev/ttyUSB0 2 48 5000000 +``` + +`engine-zeros-all` accepts `32..=64`, matching the legacy validation flow. The actual register value written is `zeros_to_find - 32`, which is what the C source used. + +## Job Exercisers + +These commands intentionally generate deterministic synthetic job material. They are designed to exercise the ASIC job path and TDM-result machinery without pretending to replace the legacy golden-vector regression harness. + +Dispatch one full grid of `WRITEJOB` packets: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + job-grid /dev/ttyUSB0 2 0 1700000000 60 5000000 +``` + +Dispatch one full grid and watch live TDM responses: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + job-grid-watch /dev/ttyUSB0 2 0 1700000000 60 5 gen2 5000000 +``` + +Dispatch back-to-back grids with the second phase offset by the legacy `MAX_EFFBST_SUBJOBS` sequence spacing: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + job-grid-2phase-watch /dev/ttyUSB0 2 0 1700000000 60 5 gen2 5000000 +``` + +## Broadcast, Multicast, and Unicast Reference + +Use unicast when you need one ASIC-local or one engine-local change: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + uart-write /dev/ttyUSB0 2 notch 0x12 01000000 5000000 +``` + +Use broadcast when every ASIC on the bus should receive the same packet: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + uart-write /dev/ttyUSB0 broadcast notch 0x12 01000000 5000000 +``` + +Use multicast when one ASIC needs the same engine-register update across a full row group: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + uart-multicast-write /dev/ttyUSB0 2 7 0x48 3c 5000000 +``` + +The higher-level helpers such as `engine-timestamp-all`, `engine-zeros-all`, and `job-grid` are implemented on top of this same routing model so developers can either stay at the helper level or drop down to raw packet control when needed. + +## Clock Diagnostics + +Read PLL and DLL status for one ASIC: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + clock-report /dev/ttyUSB0 2 5000000 +``` + +Program and lock one PLL on one ASIC: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + pll-set /dev/ttyUSB0 2 pll1 625 0 5000000 +``` + +Program, enable, and validate one DLL on one ASIC: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + dll-set /dev/ttyUSB0 2 dll1 55 5000000 +``` + +Broadcast one PLL program and verify lock across ASIC ids `0` through `15`: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + pll-broadcast-lock-check /dev/ttyUSB0 pll0 625 0 0 15 5000000 +``` + +Broadcast one DLL program and verify lock and `fincon` validity across ASIC ids `0` through `15`: + +```text +cargo run -p mujina-miner --bin mujina-bzm2-debug -- \ + dll-broadcast-lock-check /dev/ttyUSB0 dll0 50 0 15 5000000 +``` + +## Scope Boundary + +This interface is grounded in the legacy shipped UART path and the silicon validation source. It is not a JTAG debug interface and it is not a full BCH-vector regression harness.