Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3a8a05a
feat(board): per-board command channel, power-rail primitives, threa…
recklessnode Jun 11, 2026
2b981af
style(board): order power.rs items top down per S.topdown
recklessnode Aug 1, 2026
1e2c97f
feat(asic): Intel BZM2 (Bonanza Mine 2) ASIC family support
recklessnode Jun 11, 2026
efa74a5
fix(bzm2): resync parser and bound buffer on malformed serial input
recklessnode Jul 22, 2026
d41cd27
fix(bzm2): never silently drop fault frames for out-of-range ASIC ids
recklessnode Jul 22, 2026
8a3504a
fix(bzm2): time-bound diagnostic and enumeration reads
recklessnode Jul 22, 2026
d5ad3be
fix(bzm2): validate DTS/VS frames before acting on trip bits
recklessnode Jul 22, 2026
746e460
style(bzm2): order thread.rs items top down per S.topdown
recklessnode Aug 1, 2026
63caa18
style(bzm2): order uart/clock/protocol items top down per S.topdown
recklessnode Aug 1, 2026
7de822a
feat(board): BZM2 board driver with calibration and runtime tuning
recklessnode Jul 22, 2026
19d5a60
fix(bzm2): invalidate saved operating point on persistent retune
recklessnode Jul 22, 2026
61061aa
style(bzm2): order board and tuning items top down per S.topdown
recklessnode Aug 1, 2026
36dfeaa
feat(api): BZM2 diagnostic endpoints, hardware docs
recklessnode Jun 11, 2026
60b9fb1
docs(bzm2): hardware reference moves to its canonical home, bzm2-hwref
recklessnode Jul 22, 2026
58342da
docs(bzm2): accuracy fixes from claim-by-claim audit
recklessnode Jul 22, 2026
e0e6064
docs(bzm2): saved-operating-point lifecycle reflects the invalidation…
recklessnode Jul 22, 2026
ffba625
style(api): move decode_hex_payload below its callers per S.topdown
recklessnode Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
* text=auto eol=lf

*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.webp binary
*.ico binary
*.pdf binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ starting point:
(twelve BM1362 ASICs): a sister project from the 256 Foundation. An
open-source hashboard designed to be driven by open firmware.

**Experimental**

- **Intel BZM2 (Bonanza Mine 2) boards**: native Rust support for the
BZM2's 9-bit multidrop UART protocol with direct work dispatch, TDM
result handling, DTS/VS telemetry, PLL/DLL clock control, startup
calibration, and HTTP diagnostics. Targets the
[Satoshi Starter](https://github.com/Blockscale-Solutions/SatoshiStarter)
(1 ASIC) and related BZM2 board family. Functional and test-covered,
but board-specific bring-up is still maturing; not yet
production-ready. See [BZM2 docs](docs/bzm2/bzm2-port.md). Quick
start:

```bash
MUJINA_BZM2_SERIAL="/dev/ttyUSB0" \
MUJINA_BZM2_BAUD="5000000" \
cargo run -p mujina-miner --bin mujina-minerd
```

**Near-term targets**

- Installable images for the Antminer S19 series
Expand Down Expand Up @@ -221,6 +239,13 @@ GitHub.
- [CPU Mining](docs/cpu-mining.md): the CPU backend in detail
- [Container Image](docs/container.md): build and run Mujina as a
container
- [BZM2 Port](docs/bzm2/bzm2-port.md): Intel BZM2 driver architecture,
with the [tuning planner](docs/bzm2/bzm2-pnp.md) and
[opcode grounding](docs/bzm2/bzm2-opcode-grounding.md). Hardware
reference (pinout/ball map, electrical, UART/TDM protocol,
integration guide) lives in the maintained
[bzm2-hwref](https://github.com/Blockscale-Solutions/bzm2-hwref)
repository (CC-BY-SA)

### Protocols

Expand Down Expand Up @@ -252,6 +277,10 @@ GitHub.
Foundation's first open-source Bitcoin mining hashboard
- [Libreboard](https://github.com/256foundation/libreboard): 256
Foundation's open-source mining control board
- [Satoshi Starter](https://github.com/Blockscale-Solutions/SatoshiStarter):
open-source single-ASIC Intel BZM2 miner kit from Reckless Systems
- [bitaxeBIRDS](https://github.com/bitaxeorg/bitaxeBIRDS): four-ASIC
Intel BZM2 board from the Bitaxe community

## License

Expand Down
82 changes: 82 additions & 0 deletions docs/bzm2/bzm2-opcode-grounding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# BZM2 Opcode And JTAG Grounding

## Scope

This note captures only behavior that is grounded in material available to this port:

- the shipped Rust implementation in
[protocol.rs](../../mujina-miner/src/asic/bzm2/protocol.rs) and
[uart.rs](../../mujina-miner/src/asic/bzm2/uart.rs), including their
legacy wire-format tests
- the public Blockscale UART/TDM protocol reference in
[bzm2-hwref](https://github.com/Blockscale-Solutions/bzm2-hwref/blob/main/references/blockscale-uart-protocol-reference.md)

The behavior below was originally derived from the legacy `bzmd` C source
(`uart.h`, `uart.c`, and `tests/test.c`). That source is not vendored in this
repository, so any claim that traces only to it is explicitly marked below as
unverified against the legacy source.

Anything not evidenced by these sources is intentionally excluded from the Mujina port.

## 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, originally from the legacy `uart.c` and now verified against the shipped encoders and the public protocol reference:

- `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, originally from the legacy `uart.h`, `uart.c`, and `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 was reportedly the legacy model (unverified
against the legacy source); the Mujina parser treats TDM `NOOP` frames as
fixed-length and stateless, so the legacy restriction is not load-bearing
- broadcast register writes use `WRITEREG` with ASIC `0xFF`, not a separate broadcast opcode
- broadcast TDM register reads use no distinct opcode; the legacy mechanism of
layering them on top of `READREG` is unverified against the legacy source,
and the current controller exposes no broadcast TDM read helper

## What Mujina Now Grounds

Current Mujina BZM2 support in [protocol.rs](../../mujina-miner/src/asic/bzm2/protocol.rs) and [uart.rs](../../mujina-miner/src/asic/bzm2/uart.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
- any opcode semantics that cannot be traced to shipped UART code or tests

PLL debug readout is no longer excluded: UART-register-based PLL and DLL
configuration, lock polling, and status readback are implemented in
`clock.rs` and surfaced through the `thread.rs` diagnostics path.

Reason:

- the shipped Rust implementation and its wire-format tests, plus the public
protocol reference, prove the UART mining/control path
- the repository-visible sources do not provide enough packet-level JTAG detail to implement anything defensible
138 changes: 138 additions & 0 deletions docs/bzm2/bzm2-pnp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# 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 following is background as recalled from the legacy design; it is
unverified against the legacy source and is not normative for the Rust port.

The original C implementation reportedly 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, as recalled, 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/tuning/blockscale.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, modeled on the recalled legacy sweep behavior
- site-temperature-aware initial voltage and clock planning, modeled on the recalled legacy startup behavior
- 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.
It runs a single domain-ordered pass that, for each voltage domain:

- aggregates that domain's metrics
- emits the domain-level plan
- emits that domain's per-ASIC adjustments

That is O(domains x ASICs) overall, which keeps the planning work effectively
linear in ASIC count for the small domain counts on realistic hardware.

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` on the first monitor poll with no pending retune triggers
- automatically mark the saved operating point `invalidated`, with the trigger
reasons recorded and persisted, when persistent runtime retune triggers fire

A saved operating point therefore starts `pending`, validates on the first
clean monitor poll, and is invalidated in place when retune triggers persist
past the trigger tracker's threshold. Replay compatibility rejects
`invalidated` profiles, so a restart after persistent triggers falls back to
live calibration instead of replaying a known-bad operating point, and the
fresh calibration persists a new `pending` profile.

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.
Loading
Loading