Download fpga/README.md from Snapkitty/bit-string-accelerator: direct link, hf CLI and curl.
- Browser
- Download file 4.44 kB
-
https://huggingface.co/Snapkitty/bit-string-accelerator/resolve/main/fpga/README.md
- Command line
-
hf download hf://Snapkitty/bit-string-accelerator/fpga/README.md
-
curl -L -o README.md https://huggingface.co/Snapkitty/bit-string-accelerator/resolve/main/fpga/README.md
FPGA build
Runs rtl/bit_accelerator.sv on real FPGA boards. The core's 64-bit buses do
not fit on package pins, so rtl/fpga_top.sv wraps it with:
- 256 × 64-bit words of block RAM (byte addresses 0-2047). Any other address
answers with
mem_fault, so the error path works on hardware. - A UART command interface, 115200 baud 8N1.
| Board | FPGA | Clock | Top | Constraints |
|---|---|---|---|---|
| iCEBreaker | iCE40UP5K-SG48 | 12 MHz | boards/icebreaker_top.sv |
boards/icebreaker.pcf |
| ULX3S | ECP5 LFE5U-25F CABGA381 | 25 MHz | boards/ulx3s_top.sv |
boards/ulx3s.lpf |
LEDs: iCEBreaker red = last operation reported an error, green = heartbeat.
ULX3S led[0] = error, led[1] = busy, led[7] = heartbeat. The button resets.
Build and program
Ubuntu 24.04: sudo apt-get install yosys nextpnr-ice40 nextpnr-ecp5 fpga-icestorm fpga-trellis iverilog verilator.
make icebreaker # build/icebreaker.bin
make prog-icebreaker # iceprog
make ulx3s # build/ulx3s.bit (ULX3S_DEVICE=12k|25k|45k|85k to match your board)
make prog-ulx3s # openFPGALoader --board ulx3s
Then, from the host (cd host && cargo build --release):
bitacc --port /dev/ttyUSB1 ping # pong
bitacc --port /dev/ttyUSB1 write 0 0x8000000000000001
bitacc --port /dev/ttyUSB1 exec get 0 63 # 1
bitacc --port /dev/ttyUSB1 exec toggle 0 5 # 1
bitacc --port /dev/ttyUSB1 read 0 # 0x8000000000000021
bitacc --port /dev/ttyUSB1 exec get 4096 0 # error (outside the RAM)
bitacc --port /dev/ttyUSB1 selftest 10000 # acceptance test, must report 0 mismatches
The iCEBreaker's UART is the FTDI's second interface (usually /dev/ttyUSB1).
On the ULX3S it is the only FTDI port (usually /dev/ttyUSB0).
selftest writes random data to all 256 words. It then runs random operations
(including out-of-range addresses and undefined opcodes) and checks every
status and the final RAM contents against a host model of the RTL.
Protocol
All multi-byte fields are little-endian.
| Host → FPGA | FPGA → host | |
|---|---|---|
'P' |
'B' |
ping |
'W' idx[1] data[8] |
'K' |
write word |
'R' idx[1] |
data[8] | read word |
'X' op[1] base[8] offset[8] |
0xA0 | error<<1 | bit |
run one operation |
| anything else | '?' |
Results
Yosys 0.33, nextpnr 0.6, place-and-route seeds 1-5:
| Board | Logic | Block RAM | Fmax (worst of 5 seeds) | Board clock |
|---|---|---|---|---|
| iCEBreaker | 1138 / 5280 LC (21%) | 4 / 30 | 39.06 MHz | 12 MHz |
| ULX3S 25F | 1171 / 24288 LUT4 (4%), 725 FF | 2 / 56 DP16KD | 73.22 MHz | 25 MHz |
Verification
| Check | Command | Result |
|---|---|---|
| Lint, both board tops | make lint |
verilator -Wall clean |
| UART-level testbench on RTL, Icarus + Verilator | make sim |
0 failures (7554 checks under Icarus, 7407 under Verilator; their random streams differ) |
Same testbench on the iCE40 gate-level netlist (with the SB_RAM40_4K model) |
make gl-sim-ice40 |
7554 checks, 0 failures |
| Same testbench on the ECP5 gate-level netlist | make gl-sim-ecp5 |
7554 checks, 0 failures |
| Host tool against the RTL through a pseudo-terminal | make cosim |
selftest 2000 ops, 0 mismatches |
The testbench drives the serial line bit by bit. It checks pings, unknown
commands, all 256 words, directed operations (word crossing, last bit of RAM,
out-of-range reads, undefined opcodes) and 300 random operations against a
reference model. It also checks that TX idles high from power-up. Six of seven
hand-made faults in fpga_top/uart_tx are detected. The seventh (no
write-fault response) cannot occur: a write only follows a successful read of
the same word.
Two problems were found and fixed on the way:
- TX powered up low. FPGA flip-flops power up low, so TX started low and a host would read a junk byte.
- Testbench race. The testbench released reset on a clock edge, so the gate-level netlist saw a partial reset.
Limits:
- Not run on a physical board. Pin assignments are taken from each board's reference constraints and have not been checked against a board.
- ECP5 block RAM is not covered at gate level. Yosys 0.33 ships
DP16KDonly as a black box, sogl-sim-ecp5maps the memory to LUT RAM (-nobram). TheDP16KDmapping is covered by place-and-route only. The iCE40 gate-level run does include block RAM.