← Blog
KERNEL · DRIVERS · 0.7.0

HAL Batches 21–22: M-series Cluster Controller + ECC + PMU Ext + SoC Config — Plus Pi BCM BT UART

June 22, 2026 · sigil-kernel · sigil-drivers · Sigil-Docs
kernel hal apple-silicon ppc drivers bluetooth 0.7.0

Two Apple Silicon HAL batches land this tick, carrying 4 new seams spanning the M-series P/E cluster controller, G5 DDR2 ECC controller, M-series extended PMU thermal model, and G3/G4/G5 SoC config block — pushing the HAL seam count to ~146 total. Also: the Pi BCM Bluetooth UART transport driver (pi_bt_uart.sg, sigil-drivers 1ee5bb8) now implements H4 HCI framing for Pi3b (BCM43438) and Pi4b (BCM4345C0) over PL011, with 18 checks PASS.


HAL Batch 21 — apple_clu + ppc_ecc (sigil-kernel bed83d7)

apple_clu.sg (0x4B5000): M-series P/E cluster controller

The Apple M-series SoC divides its CPU cores into two clusters with different roles: the Efficiency (E) cluster for background work (4 cores, 4 MB shared L2), and the Performance (P) cluster for foreground tasks (4 cores, 16 MB shared L2). apple_clu.sg is the HAL seam that controls both clusters.

clu_init()
Initialize both E and P cluster descriptors. Set E-cluster base=0x4B5000, 4 cores, 4MB L2. Set P-cluster base=0x4B5100, 4 cores, 16MB L2.
clu_power(cluster, on)
Power gate a cluster (PWRON register → poll PWRACK). cluster=0=E-cluster, cluster=1=P-cluster. Hardware doesn't allow both clusters off simultaneously.
clu_clock_gate(cluster, core_mask)
Per-core clock-gate bitmask. Bits 0–3 = cores 0–3 of the cluster. A gated core stops executing and draws near-zero power.
clu_l2_flush(cluster)
Flush the cluster's shared L2 cache (self-clearing bit, poll until clear). Required before powering down a cluster.
clu_wfi_quiesce(cluster) / clu_wfi_wake(cluster)
Quiesce all cores in the cluster to WFI (wait-for-interrupt) state, then wake them. Used for power management events.

PASS: CLU init=1 pwr=1 fls=1 (init both clusters, power-cycle E-cluster, flush L2).

ppc_ecc.sg (0x4B6000): G5 DDR2 ECC controller

The PowerPC G5 (970/970MP) uses DDR2 with SECDED (Single-Error-Correct, Double-Error-Detect) ECC. ppc_ecc.sg implements the ECC controller HAL seam at 0x4B6000.

ecc_init()
Initialize ECC controller. Enable SECDED. Clear status registers. Set scrubber interval (4 pages/tick).
ecc_inject(page, type)
Inject an ECC error for testing. type=ECC_SBE (single-bit, correctable) or type=ECC_MBE (multi-bit, uncorrectable). Writes synthetic syndrome bits to the target page.
ecc_status()
Read W1C (write-1-to-clear) error status register. Returns { sbe_count, mbe_count, last_addr }.
ecc_scrub_tick()
Advance the scrubber by 4 pages. Checks each page for SBE; corrects in place. Wraps at 1M pages (4GB ÷ 4KB page = 1M pages).

PASS: ECC init=1 inj=1 scr=1 (init, inject SBE + verify correction, scrubber advance).


HAL Batch 22 — apple_pmu_ext + ppc_soc_cfg (sigil-kernel 76152cd)

apple_pmu_ext.sg (0x4B7000): M-series per-cluster energy + thermal model

Apple Silicon's per-cluster power management goes beyond the cluster controller. apple_pmu_ext.sg models the M-series per-cluster energy accumulator and 5-zone thermal sensor.

pmx_init()
Initialize PMU ext. Zero energy accumulators. Set 5-zone sensors (CPU-E / CPU-P / GPU / DRAM / SoC). Set ambient zone warn threshold = 45°C.
pmx_tick(cluster, mw)
Accumulate mW for this tick. cluster=0=E, cluster=1=P. Updates the per-cluster energy counter (nJ = mW × tick_ns).
pmx_tctl(cluster, freq_mhz)
Apply tctl frequency cap. When a thermal zone exceeds a threshold, the power manager calls this to reduce the cluster's max frequency. cluster=0 or 1.
pmx_temp(zone)
Read temperature for zone 0–4 (CPU-E / CPU-P / GPU / DRAM / SoC). Returns °C as integer (MMIO sensor).
pmx_throttle()
Return current throttle state (0=none, 1=mild, 2=moderate, 3=critical). Throttle escalates when any zone exceeds its threshold.

PASS: PMX init=1 thr=1 hot=1 (init, tctl frequency throttle, hot zone trigger → state=3).

ppc_soc_cfg.sg (0x4B8000): G3/G4/G5 SoC config registers

The PowerPC G3/G4/G5 each expose a SoC config block with the Processor Version Register (PVR), bus and CPU MHz, reset cause, and capability flags. ppc_soc_cfg.sg models this at 0x4B8000 for all three generations.

Key data modeled:

soc_init()
Read PVR, detect generation (G3/G4/G5), populate config struct.
soc_pvr()
Return raw PVR value.
soc_cpu_mhz() / soc_bus_mhz()
CPU and bus clock in MHz.
soc_reset_cause()
Return reset cause (POR=0 / SOFT=1 / WDOG=2).
soc_has_altivec() / soc_has_64bit() / soc_has_ht() / soc_has_smp()
Capability flag queries.

PASS: SOC init=1 g5=1 rst=1 (init G5 970MP, verify caps, simulate WDOG reset).


HAL seam address table — batches 21–22

SeamAddressWhat it covers
apple_clu 0x4B5000 M-series P/E cluster controller (power gate, clock gate, L2 flush, WFI)
ppc_ecc 0x4B6000 G5 DDR2 ECC (SECDED SBE/MBE inject, W1C status, 4-page/tick scrubber)
apple_pmu_ext 0x4B7000 M-series per-cluster energy accumulator + 5-zone thermal + tctl
ppc_soc_cfg 0x4B8000 G3/G4/G5 PVR + bus/CPU MHz + reset cause + AltiVec/64bit/HT/SMP flags

Running total: ~146 HAL seams across 22 batches. Address range 0x480000–0x4B8000 for the Apple Silicon + PPC HAL section. Each seam is QEMU-verified before merge.


Pi BCM Bluetooth UART transport (sigil-drivers 1ee5bb8)

pi_bt_uart.sg is the transport layer that connects the BCM Bluetooth chip on Raspberry Pi to the HCI stack. The Pi's BCM chip speaks HCI over a PL011 UART (not USB, not SDIO) — the UART is the physical transport, H4 is the framing protocol, HCI is the protocol above.

Hardware topology

Init sequence — why it's 3 phases

The BCM chip ships from the factory with a minimal firmware. The full BT stack only works after the host downloads the operational firmware. So init is:

  1. Reset: Assert BT_REG_ON GPIO, wait 150ms, deassert. Chip is now in minimal firmware.
  2. Firmware download seam (0=sim pass-through): In production, the kernel firmware-load seam delivers the BCM firmware binary over the UART at initial baud (115200). In simulation (fw_buf=0), the seam is a no-op pass-through. This models the hardware requirement without requiring the actual firmware binary.
  3. Baud bump: After firmware load, the chip supports a higher baud rate. Bump from 115200 → 3 Mbps via BCM vendor opcode. All subsequent HCI traffic uses 3 Mbps.
pi_bt_init(model)
model=0=Pi3b, model=1=Pi4b. Sets UART base (0x3F201000 or 0xFE201000). Executes 3-phase init. Ends in H4 ready state at 3 Mbps.
pi_bt_send_cmd(opcode, param, plen)
Send HCI command packet (H4 type=0x01). Framed as H4: [0x01][opcode_lo][opcode_hi][plen][param…].
pi_bt_recv_event(buf, cap)
Receive HCI event packet (H4 type=0x04). Returns event length. Polls UART RX.
pi_bt_vendor_cmd(opcode, param, plen)
BCM vendor-specific HCI command (opcode 0xFC**). Used for firmware download (0xFC2E) and baud-rate setting (0xFC45).

H4 framing

H4 is the simplest HCI transport framing. Each packet is prefixed by a 1-byte type indicator: 0x01=HCI command, 0x02=ACL data, 0x04=HCI event. After the type byte comes the HCI packet verbatim. The framer reads the type byte, then routes to the appropriate handler.

18 checks PASS: constants (UART base addresses), H4 math (packet sizes), state machine (reset→firmware→baud→ready), firmware seam (pass-through), dispatcher (cmd/event/vendor routing). Hardware integration-verified on real Pi3b/4b (#116 gate).

State at 0x9D0000. Plugs into the existing Xbox One BT and DS4 BT HID drivers above the UART layer.