File size: 3,028 Bytes
0ba037b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | ## How it works
A small hardware accelerator for single-bit operations on 64-bit words. It
computes the bit's address and performs a read-modify-write when needed.
effective bit = (base * 8) + offset (64-bit arithmetic)
word = effective bit / 64
bit index = effective bit mod 64
| Opcode | Operation | Writes | Result bit |
|---|---|---|---|
| 0 | GET | no | the bit |
| 1 | TEST | no | the bit |
| 2 | SET | yes | 1 |
| 3 | CLEAR | yes | 0 |
| 4 | TOGGLE | yes | the new bit |
| 5-7 | undefined | no | error |
The chip holds 4 words of RAM (byte addresses 0-31). An address outside that
range ends the operation with an error and never writes.
Everything is driven over a UART (8 data bits, no parity, 1 stop bit). All
multi-byte fields are little-endian:
| Host sends | Chip replies | |
|---|---|---|
| `P` | `B` | ping |
| `W` idx data[8] | `K` | write word idx (0-3) |
| `R` idx | data[8] | read word idx |
| `X` op base[8] offset[8] | `0xA0` + 2·error + bit | run one operation |
| anything else | `?` | |
The design is the `bit_accelerator` core from
https://github.com/SNAPKITTYAGENT9NOVA/bit-string-accelerator. Before
hardening it was checked as follows:
- a self-checking testbench, with random backpressure, read latency and fault injection
- Why3 proofs of the address arithmetic
- the same RTL running on iCE40 and ECP5 FPGAs
- a formal equivalence proof of its sky130 netlist
## How to test
1. Set the clock, and set the UART divisor pins to match it. The chip uses
`4 × d` clocks per bit, where `d = {ui[7:4], ui[2:0]}`. With all divisor
pins low it uses 104 clocks per bit, which is 115200 baud at a 12 MHz clock.
Other examples:
| Clock | Baud | d | ui[7:4] | ui[2:0] |
|---|---|---|---|---|
| 12 MHz | 115200 | 0 (default) | 0000 | 000 |
| 25 MHz | 115200 | 54 (216 clocks) | 0110 | 110 |
| 50 MHz | 115200 | 108 (432 clocks) | 1101 | 100 |
2. Connect a 3.3 V USB-UART adapter: adapter TX to `ui[3]` (RX), adapter RX to
`uo[4]` (TX).
3. Reset the design. `uo[0]` is high while an operation runs. `uo[1]` is the
last operation's error flag. `uo[2]` is its result bit.
4. Use the host tool from the repository (`fpga/host`, Rust):
bitacc --port /dev/ttyUSB0 --words 4 ping # pong
bitacc --port /dev/ttyUSB0 --words 4 write 0 0x8000000000000001
bitacc --port /dev/ttyUSB0 --words 4 exec get 0 63 # 1
bitacc --port /dev/ttyUSB0 --words 4 exec toggle 0 5 # 1
bitacc --port /dev/ttyUSB0 --words 4 read 0 # 0x8000000000000021
bitacc --port /dev/ttyUSB0 --words 4 exec get 32 0 # error (outside RAM)
bitacc --port /dev/ttyUSB0 --words 4 selftest 10000 # 0 mismatches
`selftest` runs random operations, including faults and undefined opcodes,
and checks every reply and the final RAM contents against a model of the
design.
## External hardware
A 3.3 V USB-UART adapter (for example an FTDI or CP2102 cable) on `ui[3]`/`uo[4]`.
|