A fully functional implementation of the Basic Computer I (von Neumann / Mono Architecture) described in Computer System Architecture by Morris Mano, written in Verilog HDL and simulated with Icarus Verilog and cocotb.
The design follows the classic hardwired control unit approach with a 4-bit sequence counter and a 4×16 decoder generating timing signals T₀–T₁₅. The datapath and controller are separate modules connected through a single 16-bit common bus.
┌────────────────────────┐
│ BC_I (Top) │
│ ┌──────────────────┐ │
clk ─────►│ │ Controller │ │
FGI ─────►│ │ (Hardwired CU) │ │
│ └────────┬─────────┘ │
│ Control │ Signals │
│ ┌────────▼─────────┐ │
│ │ Datapath │ ├──► PC, AR, IR
│ │ 16-bit Bus │ ├──► AC, DR, E
│ └──────────────────┘ │
└────────────────────────┘
.
├── BC_I.v # Top-level module (connects datapath + controller)
├── controller.v # Hardwired control unit
├── datapath.v # Datapath (module instantiations only)
├── alu.v # 16-bit Arithmetic Logic Unit
├── reg_unit.v # Parameterised register (LD / INR / CLR)
├── bus_mux.v # 8-to-1 parameterised multiplexer (common bus)
├── seq_counter.v # 4-bit sequence counter (timing signals)
├── memory_unit.v # 4096 × 16 word-addressable memory
├── memory_content.hex # Initial memory image (test program)
└── code.txt # Assembly source of the test program
Instantiates and wires together the datapath and controller. Exposes the following ports:
| Port | Direction | Width | Description |
|---|---|---|---|
clk |
input | 1 | System clock |
FGI |
input | 1 | Interrupt flag (input device ready) |
PC |
output | 12 | Program Counter |
AR |
output | 12 | Address Register |
IR |
output | 16 | Instruction Register |
AC |
output | 16 | Accumulator |
DR |
output | 16 | Data Register |
E |
output | 1 | Extended carry bit |
A structural module containing only module instantiations (no always or assign blocks). Registers present:
| Register | Width | Controls |
|---|---|---|
| AR | 12 | LD, INR, CLR |
| PC | 12 | LD, INR, CLR |
| DR | 16 | LD, INR, CLR |
| AC | 16 | LD, INR, CLR |
| E | 1 | LD, INR, CLR |
| IR | 16 | LD, CLR |
| TR | 16 | LD, CLR |
| IEN | 1 | LD, INR, CLR |
| R | 1 | LD, CLR (Interrupt flip-flop) |
All registers are initialised to 0 via initial blocks (no hardware reset port on the top level).
A combinational always @(*) block that decodes the timing signal T, the opcode field IR[14:12], the indirect bit IR[15], and status flags (E, Z, N, IEN, R, FGI) to drive all datapath control signals.
Execution phases:
- Fetch (T0–T1): Load AR ← PC, read memory into IR, increment PC.
- Decode (T2): Load AR ← IR[11:0] (effective address pre-fetch).
- Indirect (T3, I=1, D≠7): AR ← M[AR].
- Execute (T3+): Instruction-specific microoperations.
- Interrupt (R=1): Save PC to M[0], jump to M[1], clear IEN & R.
The sequence counter (seq_counter.v) is instantiated inside the controller and can be cleared (clr_T) to reset T back to 0 at the end of any instruction.
Parameterised (W, default 16 bits). Operations selected by op[2:0]:
op |
Operation | Description |
|---|---|---|
000 |
ADD | AC + DR → out, updates CO, OVF, E |
001 |
AND | AC ∧ DR → out |
010 |
Transfer DR | DR → out |
011 |
Complement AC | ~AC → out |
100 |
Shift Right | {E, AC[W-1:1]} → out, AC[0] → E |
101 |
Shift Left | {AC[W-2:0], E} → out, AC[W-1] → E |
110 |
Transfer AC | AC → out (default/passthrough) |
Status outputs Z (zero) and N (negative) are updated for every operation; CO and OVF are meaningful only for ADD.
Positive-edge-triggered register with synchronous reset, write enable, and increment:
| Reset | WE | INC | Operation |
|---|---|---|---|
| 0 | 0 | 0 | Retain |
| 0 | 0 | 1 | A ← A + 1 |
| 0 | 1 | X | A ← DATA |
| 1 | X | X | A ← 0 |
Parameterised (W, default 16 bits). Selector mapping inside the datapath:
sel |
Source |
|---|---|
| 0 | 0x0000 |
| 1 | AR |
| 2 | PC |
| 3 | DR |
| 4 | AC |
| 5 | IR |
| 6 | TR |
| 7 | Memory read data |
4-bit counter that produces timing signals T0–T15. Increments on every positive clock edge; resets to 0 when clr_T is asserted by the controller.
4096 × 16 synchronous-write, combinational-read RAM. Initialised from memory_content.hex using $readmemh.
| Opcode | Symbol | Description |
|---|---|---|
| 000 | AND | AC ← AC ∧ M[X] |
| 001 | ADD | AC ← AC + M[X], E ← carry |
| 010 | LDA | AC ← M[X] |
| 011 | STA | M[X] ← AC |
| 100 | BUN | PC ← X |
| 101 | BSA | M[X] ← PC, PC ← X+1 |
| 110 | ISZ | M[X]++; skip if zero |
| Code | Symbol | Description |
|---|---|---|
| 7800 | CLA | Clear AC |
| 7400 | CLE | Clear E |
| 7200 | CMA | Complement AC |
| 7100 | CME | Complement E |
| 7080 | CIR | Circular shift right (AC, E) |
| 7040 | CIL | Circular shift left (AC, E) |
| 7020 | INC | Increment AC |
| 7010 | SPA | Skip if AC positive |
| 7008 | SNA | Skip if AC negative |
| 7004 | SZA | Skip if AC zero |
| 7002 | SZE | Skip if E zero |
| 7001 | HLT | Halt |
| Code | Symbol | Description |
|---|---|---|
| F080 | ION | Interrupt enable |
| F040 | IOF | Interrupt disable |
Note: INP, OUT, SKI, SKO are not implemented.
When IEN = 1, FGI = 1, and R = 0, the interrupt flip-flop R is set after T2. The interrupt cycle then executes:
T0: AR ← 0
T1: M[AR] ← PC, PC ← 0
T2: PC ← PC + 1, IEN ← 0, R ← 0, SC ← 0
The ISR vector is expected at M[1] (a BUN instruction pointing to the service routine). The return address is saved at M[0].
The sample test program (code.txt) exercises memory-reference, register-reference, and interrupt instructions. Its machine code is pre-loaded into memory_content.hex.
BUN 0x2 ; Return save address slot
BUN 0x400 ; ISR vector
CLE ; Program start
LDA 0x600
ADD 0x601
1 ADD 0x602 ; Indirect add
1 AND 0x603 ; Indirect AND
SPA
CLA
STA 0xaea
CMA
ADD 0xaea
SNA
CLA
ION
SZA
BUN 15 ; Loop until interrupt
CME
CIL
CIR
HLT
ORG 0x400 ; Interrupt Service Routine
INC
ION
1 BUN 0x0 ; Return via indirect BUN
ORG 0x600 ; Data
441
445
0xaeb
0xaea
ORG 0xAEA ; More data
348
447# Icarus Verilog
sudo apt install iverilog
# cocotb
pip install cocotbiverilog -o bc_sim BC_I.v controller.v datapath.v alu.v reg_unit.v \
bus_mux.v seq_counter.v memory_unit.v
vvp bc_simNo testbench is provided — you are expected to write your own using cocotb. The top-level DUT is BC_I, which exposes the following ports for you to drive and monitor:
| Signal | Direction | Width | Notes |
|---|---|---|---|
clk |
input | 1 | Drive with a clock generator |
FGI |
input | 1 | Assert to trigger an interrupt |
PC |
output | 12 | Monitor for control flow |
AR |
output | 12 | Monitor for memory addressing |
IR |
output | 16 | Monitor fetched instructions |
AC |
output | 16 | Monitor computation results |
DR |
output | 16 | Monitor data register |
E |
output | 1 | Monitor carry/extend bit |