feat(asic): Intel BZM2 (Bonanza Mine 2) ASIC family support - #69
Open
recklessnode wants to merge 9 commits into
Open
feat(asic): Intel BZM2 (Bonanza Mine 2) ASIC family support#69recklessnode wants to merge 9 commits into
recklessnode wants to merge 9 commits into
Conversation
Author
This was referenced Jun 11, 2026
recklessnode
force-pushed
the
bzm2/pr2-asic-core
branch
from
July 22, 2026 03:45
a48d89a to
a321534
Compare
Author
|
Series merge order (updated 2026-07-22) - four stacked PRs, all MERGEABLE, each green under
The order is the compile dependency chain (verified at import level - no PR references a symbol from a later one), not a preference. Merging #68 first collapses #69's visible diff to its own commits, and so down the stack. Later commits added today (the runtime-retune invalidation fix on #70, the serial hardening on #69) are folded into the PR whose code they touch, keeping each PR correct-from-first-commit rather than fixed-in-a-later-PR. |
…d telemetry types
Infrastructure groundwork usable by any board, no new dependencies:
- Wire the per-board command channel end to end: BackplaneConnector and
api::registry::BoardRegistration gain an optional
mpsc::Sender<BoardCommand>; the backplane forwards it at start_board.
BoardCommand::SetFanTarget and SetFanTargetRequest existed but nothing
wired them - PATCH /api/v0/boards/{name}/fans/{fan} now drives them
(boards opt in by populating command_tx; all current boards answer
"accepts no commands" until they grow a command loop).
- BoardRegistry::board(name) + command_tx(name) accessors with lazy
disconnect pruning; get_board refactored onto the former.
- board/power.rs: PowerRail trait (Tps546PowerRail, FilePowerRail file
adapters, FileGpioPin) + GpioResetLine (AsicEnable impl) +
VoltageStackBringupPlan for ordered multi-rail bring-up with settle
delays and reverse-order shutdown.
- hash_thread: HashThreadTemperatureReading/PowerReading/
TelemetryUpdate + HashThreadEvent::TelemetryUpdate (typed path for
thread-sourced sensor data; bitaxe's monitor TODO wants this) and
HashThreadError as a shared thread error vocabulary.
- Backplane::attach_configured_board: attach an env-configured virtual
board without synthesizing a transport event.
- BoardTelemetry.asics (serde-default) + AsicState/EngineCoordinate:
generic per-ASIC topology/diagnostics state for multi-ASIC boards.
- transport/serial: Clone derives on the Arc-backed reader/writer/
control halves.
- .gitattributes for LF normalization.
Applies the CODE_STYLE S.topdown/S.mod ordering rules added in e0e582f to the new power-rail module: - `pub trait PowerRail` now precedes `PowerRailTelemetry`, the supporting type that appears in its signature (S.topdown main-type-first). - `pub async fn pulse` now precedes the private `drive` it calls (S.topdown pub-before-private / caller-before-callee). - `impl AsicEnable for GpioResetLine` moves back next to `GpioResetLine` instead of being stranded below the unrelated `FileGpioPin` block (S.mod group order). - `VoltageStackBringupPlan` is reunited with its two impls, which had been split off below `FilePowerRail` (S.mod group order). Pure reordering: the file is line-for-line identical as a multiset apart from two blank lines rustfmt collapsed. cargo fmt + cargo check clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The protocol/transport layer for the Intel BZM2 mining ASIC, mirroring the bm13xx in-tree structure (protocol codec / controller / thread): - protocol.rs: pure codec - opcodes, frame encoders (write_job, write_register, noop, loopback, read_result), TDM frame/result parsers, the 20x12 logical engine grid (default 236 active engines), target -> leading-zeros conversion. Deps: bitcoin + std only. - uart.rs: Bzm2UartController over the 9-bit multidrop UART - register R/W (unicast/multicast/local), NOOP "BZ2" verification, chain enumeration (ID-assignment walk from the 0xFA default), TDM enable/sync reads, engine-map discovery, loopback, DTS/VS sensor configure + query. - clock.rs: Bzm2ClockController - PLL/DLL program/enable/lock-wait (per-ASIC and broadcast), clock debug reports. - thread.rs: Bzm2Thread implementing HashThread - direct UART work dispatch, TDM result handling with per-ASIC/per-PLL hashrate estimation, DTS/VS telemetry via HashThreadEvent::TelemetryUpdate, thermal-trip frame handling, diagnostic command handle. Tests are PTY-based chain emulations, gated #[cfg(all(test, unix))] so non-Unix hosts still build the crate cleanly.
The long-lived TdmFrameParser never registers an expected READREG length, so any `<asic<100> 0x03` prefix in the stream hit the no-count `break` with cursor at 0: nothing was drained, framing wedged permanently, and self.buffer grew by up to a full read on every push (line-noise OOM). Treat a READREG with no pending read as a stray byte and resync one byte forward, matching the unknown-opcode arm. This makes strictly-forward progress, keeps the buffer bounded to a single partial frame, and preserves correct framing of valid frames. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The `asic >= 100` resync heuristic dropped every frame from an id at or above 100, including DTS/VS frames carrying the thermal-trip / voltage-fault bits. Ids that high are not reached by the supported 1/4/9/12 chains (start_id defaults to 0), but an operator can push them via MUJINA_BZM2_ENUM_START_ID, at which point over-temp protection was silently disabled for the whole bus. Exempt DTS/VS frames from the id heuristic so a trip/fault frame always reaches handle_dts_vs_frame, where it is logged loudly before any protective action. The heuristic still skips stray high-id bytes for the non-safety opcodes, so resync behaviour for line noise is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The single-task actor ran its diagnostic command handlers (read_register, query_noop, query_loopback, and the 12 reads behind query_clock_report) on bare read_exact calls. A chip that never answers, answers short, or goes quiet mid-response froze the whole actor -- dispatch ticks, result handling, DTS/VS trip detection, and even ThreadCommand::Shutdown -- permanently. Wrap each diagnostic read in a bounded tokio timeout (DIAGNOSTIC_READ_TIMEOUT) via a shared helper, mapping expiry to DiagnosticsFailed, mirroring the streaming helpers. enumerate_chain likewise verified the freshly assigned id with the unbounded verify_noop_bz2; a device that passed the timed 0xfa probe but then failed to echo on its new id wedged enumeration and board init. Give the post-assign verify the same probe_timeout bound. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A single DTS/VS frame with any of thermal_trip / thermal_fault / voltage_fault / voltage_shutdown set instantly and permanently killed the hash thread. Those bits all live in one payload byte with no debounce and no validity gate, so line noise on a multidrop bus (or one crafted frame) could take a board offline until a full miner restart -- DoS-by-noise. Gate each fault decision on the matching sensor-enable bit, which reflects host configuration and is set on every genuine frame. A real over-temp still stops immediately (no debounce delay), while a stray trip bit from a disabled sensor is ignored. The residual single-frame-both-bits risk is documented in a code comment; the protocol carries no checksum to close it fully. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Applies the CODE_STYLE S.topdown/S.mod ordering rules added in e0e582f. thread.rs was the most inverted module in the series: - `pub struct Bzm2Thread` - the module's subject - was the LAST of eleven types, below every supporting type. It and its two impls now open the type region (S.topdown main-type-first). - `pub struct Bzm2ThreadHandle` sat below the three private measurement types; it now precedes them, so all public types come before the private ones (S.topdown pub-before-private). - Four constants were scattered: three below `Bzm2ThreadConfig` and `DIAGNOSTIC_READ_TIMEOUT` ~1000 lines into the function group. All four now sit in the constants group after the imports (S.mod group order). - `fn pll_index_for_row` was wedged between two types; moved into the function group (S.mod group order). - `read_exact_diagnostic` was defined above all three of its callers (read_register, query_noop, query_loopback); moved below them (S.topdown caller-before-callee). Pure reordering: identical line-for-line as a multiset apart from one blank line rustfmt collapsed. cargo fmt + cargo check clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Continues the S.topdown pass (e0e582f) across the rest of the ASIC core: - uart.rs: `Bzm2UartController`, the module's subject, was the last of five types, below the config, error and engine-map types that appear in its signatures. It and its impl now open the type region. - clock.rs: same shape - `Bzm2ClockController` sat below all eight of its supporting types (Bzm2Pll/Bzm2Dll, the config and status structs); moved to the head of the type region. - protocol.rs: `Bzm2EngineLayout` and its two impls were stranded in the middle of the free-function group, between `default_engine_coordinates` and `leading_zero_threshold`. Moved up with the other types so the function group is contiguous (S.mod group order). Pure reordering in all three files - identical as a multiset of lines apart from blank lines rustfmt normalised. cargo fmt + cargo check clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
recklessnode
force-pushed
the
bzm2/pr2-asic-core
branch
from
August 1, 2026 14:13
9453645 to
8a79ca6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 2 of the BZM2 series (stacked on part 1 — review the
feat(asic)commit). Adds the ASIC-family layer for the Intel BZM2 (Bonanza Mine 2), the chip the 256 Foundation received as a 54,000-unit grant and that the Satoshi Starter / bitaxeBIRDS board family is built around.The layout mirrors the in-tree
bm13xxstructure (pure codec → controller →HashThread):protocol.rs— opcodes, frame encoders (write_job,write_register,noop,loopback,read_result), TDM frame/result parsers, the 20×12 logical engine grid (236 active engines by default), target→leading-zeros conversion. Depends onbitcoin+ std only.uart.rs—Bzm2UartControllerfor the chip's 9-bit multidrop UART: register R/W (unicast/multicast/local), NOOP"BZ2"verification, chain enumeration (ID-assignment walk from the0xFApower-on default), TDM enable and synchronized reads, engine-map discovery, loopback, and DTS/VS (on-die temperature/voltage sensor) configure + query.clock.rs—Bzm2ClockController: PLL/DLL program/enable/lock-wait, per-ASIC and broadcast, plus clock debug reports.thread.rs—Bzm2ThreadimplementingHashThread: direct UART work dispatch, TDM result handling with per-ASIC/per-PLL hashrate estimation, DTS/VS telemetry emitted viaHashThreadEvent::TelemetryUpdate(from part 1), thermal-trip frame handling, and a diagnostics handle used by the board layer in part 3.Unlike the BM13xx family, the BZM2 is publicly documented — the protocol reference and integration guide land with part 4 (CC-BY-SA), so this driver can be reviewed against the chip's actual documentation rather than reverse engineering.
Tests are PTY-based chain emulations (multi-ASIC enumeration, TDM result paths, DTS/VS frames), gated
#[cfg(all(test, unix))]so non-Unix hosts build the crate cleanly.Gates:
cargo build(0 warnings),cargo test(362 passed / 0 failed),cargo fmtclean.🤖 Generated with Claude Code
Serial-robustness hardening (added 2026-07-22)
An internal adversarial audit of this PR's serial parser/actor against hostile and malformed
multidrop-UART input surfaced five defects in the code this PR introduces. They are fixed here
as discrete, individually-tested
fix(bzm2):commits on top of thefeat(asic)commit, so the ASIClayer is robust on a noisy real-world bus from the first commit rather than being hardened in a later
PR. Each is independently reviewable and CI-green per commit.
protocol.rs): aREADREG-looking prefix withno pending read wedged framing at cursor 0 and grew the buffer on every read. Now resyncs one byte
forward (matching the unknown-opcode arm), keeping strictly-forward progress and a bounded buffer.
protocol.rs): theasic >= 100resyncheuristic dropped DTS/VS trip/fault frames too — reachable via
MUJINA_BZM2_ENUM_START_ID, whichsilently disabled over-temp protection for the bus. DTS/VS frames are now exempt from the heuristic.
thread.rs,uart.rs): bareread_exactin thediagnostic handlers and in
enumerate_chain's post-assign verify let a silent chip freeze thesingle-task actor (including
Shutdown) or wedge board init. Both are now time-bounded.thread.rs): a single frame with a fault bit setkilled the thread permanently — DoS-by-noise. Fault decisions are now gated on the matching
sensor-enable bit; a genuine over-temp still stops immediately (no debounce). Residual risk noted
in a code comment (the protocol carries no checksum).
Two lower-severity findings (generation-mismatch mis-framing; 1-bit nonce-replay double-count) are
deferred with rationale — both would need invasive changes that risk dropping valid frames/shares,
and the safety-relevant consequence of the first is already mitigated by the DTS/VS enable-gate.
A scheduler thread-respawn observation is upstream's own code and out of scope for this series.