Commit Graph

14 Commits

Author SHA1 Message Date
Roflin 991342c0b5 docs: geom_honoured is asserted but not wired anywhere yet
The ring-geometry note claimed a rejected BP/RHBP is "observable rather than
silent".  That is overstated: geom_honoured exists on RXFrameAssembler and the
tests check it, but BBATop wires it to nothing -- no status register, no LED,
no UART line -- so a rejected geometry is silent to both the GC and the bench.
Recorded as a loose end; the guarantee today is "rejected rather than obeyed",
not "reported".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:24:53 +02:00
Roflin 6b2e6724a2 Fix the 0/8 timing failure the ring-geometry change caused
The sweep after honouring BP/RHBP came back clk 16.5-17.6 MHz, 0/8 seeds
passing against the 24 MHz target -- from ~29 MHz before.  The build was
unusable.

Cause: the sanitised bounds were combinational, so

  bp/rhbp -> comparators -> Mux -> subtract -> pg_total -> add -> raw
          -> compare -> free -> compare -> have_room

became ONE path where it had been constant-folded.  Two fixes were needed, and
the first alone was not enough:

1. Register pg_first/pg_last/pg_total and rrp_s.  They change only when the GC
   writes BP/RHBP/RRP, so a cycle of staleness costs nothing, and `free`
   already reaches the FSM through the registered have_room.  This got it to
   ~20 MHz, still failing.
2. Narrow them from 8 bits to 4.  Ring pages are 1-15.  An 8-bit variable-vs-
   variable comparison is a carry chain where variable-vs-CONSTANT folds into
   a couple of LUTs -- precisely what making the bounds dynamic had thrown
   away.  geom_ok guarantees bp < rhbp <= 0x0F, which makes the narrow slice
   safe.

Now 26.90 and 28.50 MHz on seeds 1-2, against 28.00/29.21 for the build
without the geometry change.

Attribution needed an A/B: the register-space decode (item 4) landed in the
same stretch and had never been swept on its own.  Measured separately it
closes 28.0-29.2 MHz and costs nothing, so the whole regression was the
assembler.  The critical-path report named rx_frame_assembler.py:215 -- the
rrp clamp -- which is what pointed at the comparator width rather than at the
arithmetic everyone would suspect first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:16:43 +02:00
Roflin 147ef8fcfc Honour the GC's ring geometry (BP / RHBP) instead of hardcoding it
Compat TODO item 2, the last silent-corruption risk.  RXFrameAssembler took
the RX ring as a compile-time constant (pages 1-15), so a driver configuring a
SMALLER ring was ignored and we kept writing pages it would never read, with
nothing reporting an error.  The bounds now come from bp/rhbp, wired in BBATop
from the GC's BP (0x0A/0B) and RHBP (0x1A/1B) registers, and both the page wrap
and the free-space calculation use them.

Garbage is rejected rather than obeyed: the driver's values are used only when
bp >= 1 (page 0 is the TX buffer), rhbp <= 0x0F and rhbp > bp.  Otherwise the
hardcoded layout stands and geom_honoured goes low, so a mismatch is
observable.  Without that check BP=0 or an RHBP below BP gives a zero or
negative ring size and the assembler writes outside it.

⚠️ Recorded as a known limitation: a ring must be LARGER than
_MAX_FRAME_PAGES (6) or nothing is ever stored, because a frame's length is
unknown until EOF so the worst case (1518+4 = 6 pages) is reserved up front.
Found the honest way -- the test was written with RHBP=4 first and passed
VACUOUSLY, storing nothing at all.  Making that work needs a length-aware
reservation, which is a bigger change than this one.

T7 (8-page ring honoured, frames only in pages 1-8, and at least one actually
stored) and T7b (all three garbage cases rejected, fallback still stores).
Mutation-tested: wrapping at the hardcoded page 15 writes pages 9-11 and fails
T7; removing the sanity check fails all four T7b assertions.

Item 1 (RX filtering) remains the only open gap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:06:04 +02:00
Roflin 4a455bbe88 Decode NCRB and the registers with non-zero hardware defaults
Compat TODO items 3 and 4.  Every register libogc2 writes now stores writes and
reads back, with the documented hardware reset defaults: NCRB (0x01), BP
(0x0A/0B), TLBP (0x0C/0D), RXINTT (0x14/15), RHBP (0x1A/1B), NWAYC (0x30,
default 0x84), GCA (0x32), MISC (0x3D, default 0x3C), MISC2 (0x50).  BP and
RHBP reset to OUR layout (page 1, page 15) so a driver reading them back
without writing sees something consistent with where the ring actually is.

Previously all of these read 0x00 and dropped writes, so a driver doing
read-modify-write or sanity-checking a default saw zero -- and libogc2 does
read NWAYC before writing it.

Deliberately still reading 0x00: LTPS, LRPS, TWP, TRP, TXFIFOCNT.  Those are
STATUS registers, not configuration, so storing what was written to them would
be a lie rather than a default.  Drivers read LRPS/LTPS only on error bits and
0x00 reads as "no error", which is truthful for a device that does not detect
them.

NCRB, BP and RHBP are also exported as BBARegisterFile.ncrb/.bp/.rhbp for the
RX-filtering work (item 1) and the ring-geometry check (item 2).  Nothing
consumes them yet -- the decode lands first so the behaviour that uses it is a
separate, testable change.

T15 checks the defaults, replays libogc2's twelve init writes in its order and
reads every one back, and asserts the NCRB export is 0x52 with PR=0, PM=0,
AB=1 -- i.e. that the real BBA delivers own-MAC + broadcast only, which is the
fact item 1 has to act on.  Mutation-tested: dropping NWAYC's 0x84 default
fails it, as does dropping NCRB writes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 14:02:28 +02:00
Roflin e10849f73d docs: track the BBA compatibility gaps as TODOs
Records the differences between this implementation and what real GC software
expects, with the evidence and the driver source for each, so they are worked
through rather than rediscovered.  Open items: no hardware RX filtering,
hardcoded ring geometry, NCRB undecoded, and registers that read 0x00 where
real hardware has non-zero defaults.  Also records what is deliberately NOT to
be implemented (the device-initiated challenge/response) and that two of our
registers (TWD, HIPR) do not exist on real hardware.

Notes explicitly that games use the official Nintendo SDK driver, which none of
the three readable drivers is, so "no open driver does this" is weaker evidence
than "the hardware has no such register".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 11:40:55 +02:00
Roflin d83f7beef8 Fix the RX ring descriptor to the format libogc actually parses
The assembler wrote 00 00 len[11:8] len[7:0] with bytes 0 and 1 zero.  The real
format, read out of libogc's own BBA driver, is a PACKED 32-BIT LITTLE-ENDIAN
word: next_packet_ptr in bits 0-11, packet_len in bits 12-23, status in bits
24-31.  Under that driver our descriptor for a 342-byte frame decoded as a
12-byte frame with a next-pointer of 0 into the reserved page, so Swiss and
anything else built on libogc would have initialised perfectly and then
misparsed every received frame.

Found by disassembling /opt/devkitpro/libogc/lib/cube/libbba.a (gcif.o,
bba_interrupt) rather than by guessing:

  lwbrx   r9,0,r9              # 4 bytes as a little-endian word
  rlwinm  r4,r9,20,20,31       # packet_len = (word >> 12) & 0xFFF
  addi    r4,r4,-4             # payload = packet_len - 4
  cmpwi   r4,1540
  srwi    r9,r9,24             # status = word >> 24
  and.    r9,r9,r18            # r18 = 0x..84 -> errors are 0x80 | 0x04

and the low 12 bits are written straight back to RRP (0x0018/0x0019), which is
why next_packet_ptr has to be there at all -- it is the driver's read pointer.

The length semantics and addressing were already correct (descriptor at
page<<8, body at +4, length including the descriptor); only the packing was
wrong and next_packet_ptr was never written.

RXFrameAssembler now computes the next page BEFORE writing the descriptor
(CALC_NEXT, formerly ADVANCE_RWP, moved ahead of the backfill) and writes bytes
0-3 as WRITE_D0..WRITE_D3.

WRITE_D3 fixes a latent bug this exposed: WRITE_PLACEHOLDER issues four writes
but the first lands twice, so desc_base+3 was NEVER written.  That was harmless
while the length write covered byte 3.  As the status byte it would have been
whatever the previous frame left in that cell, and a stale 0x80 or 0x04 makes
the driver discard the frame.

Readers updated to decode identically: netboot (which also now takes RRP from
the descriptor's own next_packet_ptr, as libogc does, instead of recomputing
the page span) and t_dma, including its page map.  netboot's +0/+1 offset probe
is removed -- the underrun that shifted descriptors is fixed and confirmed on
console, and under a packed format a +1 read is not a shifted descriptor but
garbage.

The assembler's testbench now decodes with libogc_decode(), reproducing the
instruction sequence above, so the tests check the DRIVER'S algorithm rather
than our own packing.  Every module testbench and bba_top at EXI_HALF=1/2/8
pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-10 10:27:45 +02:00
Roflin f2762f613d docs: record the REAL GC<->BBA EXI protocol; current header format is wrong
Read from the two drivers that will actually talk to this device: libogc
(libdb/uIP/bba.c) and Swiss (cube/patches/bba/bba.c).  The implemented 2-byte
header with a 2-bit length field does not match either.

The real protocol has two address spaces selected by bit 7 of byte 0, and no
length field at all -- the data phase runs until CS:

  COMMAND  (6-bit reg)   one 4-byte readwrite; response in byte 3
  REGISTER (16-bit reg)  [0x80|w<<6, reg>>8, reg&0xFF, 0x00] + data phase

NCRA, IR, NWAYS, RWP, RRP and PAR all live in the register space.  The
mismatch is destructive rather than merely non-functional: Swiss reading
NWAYS sends 80 00 31 00, which the current decoder reads as 'write 0x31 to
NCRA' -- and bit 0 of 0x31 is RESET.  Every register read would reset the
device.  The device-ID query is the one case where the two formats coincide,
which is exactly why enumeration worked on hardware and nothing else would
have.

Also explains the 22-bit response latency measured on console: the trailing
0x00 byte in the real header is the device's prefetch window, and the current
format spends those bits on the length field, leaving zero slack.  Adopting
the real format is required for compatibility anyway and recovers ~296 ns of
the ~815 ns deficit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 11:47:50 +02:00
Roflin fe96876a80 Fix EXI DO/DI swap: rename nets + swap damping resistors, no copper changes
SP1 DO/DI naming is console-perspective (verified against SD Gecko and
SD2SP2 wiring): pin 6 DO = GC data out = adapter MOSI input, pin 9 DI =
GC data in = adapter MISO output. The design doc's SP1 table had the
roles reversed and the schematic followed it, putting the FPGA's MISO
driver on the GC-driven DO line (bus contention).

Fix keeps all PCB routing identical:
- Swap net names EXI_MOSI(_RAW) <-> EXI_MISO(_RAW) across exi/Fpga/root
  sheets and the PCB (also renames the D2 PUSB3AB6 channel assignments).
- Swap R21 (22R) <-> R25 (33R) identities between the two footprint
  positions so 33R stays on the FPGA-driven MISO line, 22R on the MOSI
  input.
- Correct the SP1 pin table + MISO-tristate note in
  docs/gc_bba_fpga_design.md (root cause).

Resulting board pin map (for the future board PCF): EXI_MOSI = FPGA
pin 4 (IOB_8a, input), EXI_MISO = pin 3 (IOB_9b, output).

Verified: ERC 0, DRC 0 errors with schematic parity 0, netlist shows
J3.6(DO)->R21->pin 4 and J3.9(DI)->R25->pin 3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 12:36:41 +00:00
Roflin a7c88109a9 Started on component selection of the final PCB. 2026-06-13 22:09:05 +02:00
Dennis Brentjes 8d0ab1d948 Added full design created with Claude 2026-06-13 18:35:38 +02:00
Roflin 6a94867f30 Moved media folder to LFS 2025-09-27 10:18:17 +02:00
Roflin ac755932d5 Added Debouncer ToggleButton and PulseButton 2025-09-20 22:44:04 +02:00
Roflin eea189ae61 Start of a new debug connector 2025-09-19 12:06:05 +02:00
Roflin 7b5ecda692 Initial commit 2025-09-19 10:18:10 +02:00