
ASAP CPU v2 is a SAP-1-style 8-bit computer, in the spirit of Ben Eater's breadboard CPU. It contains:
pc.v),mar.v),ram.v),ir.v),register.v),alu.v),out_reg.v),display.v),controller.v) driven by a 6-step T-state
ring counter.All blocks exchange data over a single 8-bit bus (a priority mux in
project.v — no internal tri-states). Every instruction runs in 6 clock
cycles: T0–T1 fetch the instruction (MAR ← PC, then IR ← RAM[MAR] and
PC ← PC+1), T2… execute, and any leftover steps are NOPs.
Each RAM byte is an instruction: opcode in bits [7:4], a 4-bit RAM address in
bits [3:0].
| Opcode | Mnemonic | Operation |
|---|---|---|
0x0 |
LDA n |
A ← RAM[n] |
0x1 |
ADD n |
A ← A + RAM[n] |
0x2 |
SUB n |
A ← A − RAM[n] |
0xE |
OUT |
OUT register ← A |
0xF |
HLT |
stop the CPU |
Any other opcode behaves as a NOP. HLT latches the CPU into a halted state
until reset; the display keeps refreshing.
The OUT register is shown as a decimal number (0–255) across three discrete
single-digit 7-segment displays — three separate parts, not one 3-digit
module. display.v converts the value to BCD (double-dabble) and time-
multiplexes the digits over a shared segment bus:
uo[6:0] — segment lines a…g, wired in parallel to all three displays,
active high (uo[0] = a = "SEG 1", …, uo[6] = g = "SEG 7").uo[7] — digit select 1 → the ones display, active high.uio[0] — digit select 2 → the tens display, active high.uio[1] — digit select 3 → the hundreds display, active high.Exactly one digit-select line is high at a time and it advances every clock, so
at the rated 1 MHz all three displays look rock-steady. (uio_oe is 0x03:
only uio[1:0] are driven as outputs.)
uio[6] is the mode pin: 0 = RUN, 1 = PROGRAM.
uio[5:2] is the address,
ui[7:0] is the data byte, and the synchronized rising edge of uio[7]
writes RAM[uio[5:2]] ← ui[7:0]. Hold address and data stable for at least
two clock cycles before raising uio[7] and until at least one clock after
lowering it.uio[7] is a reset (clears the PC and registers;
RAM keeps its contents). The external rst_n pin asynchronously resets the
CPU/display state in either mode.uio[6] = 1 (PROGRAM mode), pulse rst_n low then high.uio[5:2] and the data on
ui[7:0], wait at least two clock cycles, raise uio[7], wait at least two
clock cycles, then lower uio[7].uio[6] = 0 (RUN mode), wait at least two clock cycles, and pulse
uio[7] high then low — this resets the PC to 0 and the CPU starts running.Example: program LDA 14 ; ADD 15 ; OUT ; HLT at addresses 0–3, with
RAM[14] = 5 and RAM[15] = 3. After ~24 clocks the display reads 008.
The cocotb testbench in test/ exercises exactly this kind of sequence
(add, subtract, chained add, and that HLT freezes the output).
Three single-digit common-cathode 7-segment displays (three separate parts, not
one 3-digit module). Wire the seven segment lines uo[6:0] to the matching
segment pins (a…g) of all three displays in parallel. Route each display's
common-cathode pin to ground through a low-side NPN transistor whose base is
driven by that display's select line: uo[7] for the ones digit, uio[0] for
tens, uio[1] for hundreds. Segments and selects are active high — if your
displays are common-anode, invert the segment and select lines instead. With no
external hardware you can still watch the multiplexed segment pattern on the
GPIO, or wire plain LEDs. To drive the program-mode inputs you also need eight
data switches (ui[7:0]), four address switches (uio[5:2]), and the mode /
write-reset switches (uio[6], uio[7]).
| # | Input | Output | Bidirectional |
|---|---|---|---|
| 0 | RAM BIT 1 | SEG 1 | SEG SELECT 2 (OUT) |
| 1 | RAM BIT 2 | SEG 2 | SEG SELECT 3 (OUT) |
| 2 | RAM BIT 3 | SEG 3 | ADDRESS BIT 1 (IN) |
| 3 | RAM BIT 4 | SEG 4 | ADDRESS BIT 2 (IN) |
| 4 | RAM BIT 5 | SEG 5 | ADDRESS BIT 3 (IN) |
| 5 | RAM BIT 6 | SEG 6 | ADDRESS BIT 4 (IN) |
| 6 | RAM BIT 7 | SEG 7 | MODE (0 = RUN, 1 = PROGRAM) (IN) |
| 7 | RAM BIT 8 | SEG SELECT 1 | WRITE[PROGRAM MODE] | RESET[RUN MODE] (IN) |