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]`.