diff --git a/README.md b/README.md
index aa15ebb..d7c3021 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,271 @@
+# Verilog Simple RISC Processor
+
+> A modular, multi-cycle, accumulator-based RISC processor implemented in Verilog HDL for computer architecture education and digital system design.
+
+
+
+
+
+
+---
+
+## Overview
+
+This repository contains a complete RTL implementation of a simple accumulator-based Reduced Instruction Set Computer (RISC) processor written in synthesizable Verilog HDL.
+
+The project is designed for education and experimentation. It demonstrates:
+
+- processor organization and RTL design;
+- separation of datapath and control logic;
+- multi-cycle instruction execution;
+- finite-state-machine control;
+- unified instruction and data memory;
+- module-level and CPU integration verification;
+- waveform-based debugging and architectural analysis.
+
+The design prioritizes readability, modularity, and ease of extension rather than performance. Each hardware component is implemented as an independent RTL module.
+
+---
+
+## Key Features
+
+- 8-bit accumulator-based datapath
+- 8-bit instructions with a 3-bit opcode and 5-bit operand
+- 32-address unified program and data memory
+- Eight native instructions
+- Multi-cycle finite-state-machine controller
+- Modular and synthesizable Verilog RTL
+- Automated regression and CPU integration tests
+- VCD waveform generation for GTKWave
+- Vivado RTL elaboration and synthesis support
+
+---
+
+## Processor Specification
+
+| Feature | Description |
+|---|---|
+| Architecture | Accumulator-based RISC |
+| Datapath width | 8 bits |
+| Instruction width | 8 bits |
+| Opcode width | 3 bits |
+| Operand width | 5 bits |
+| Address space | 32 memory locations |
+| Memory organization | Unified program/data memory |
+| Controller | Finite-state machine |
+| Execution model | Multi-cycle |
+| RTL language | Verilog HDL |
+| Primary simulator | Icarus Verilog |
+
+---
+
+## Architecture
+
+The processor consists of a compact datapath and a finite-state-machine controller. Because the architecture is accumulator-based, arithmetic and logical operations use a single general-purpose register: the accumulator (`AC`).
+
+```mermaid
+flowchart LR
+ PC[Program Counter]
+ IR[Instruction Register]
+ MUX[Address Multiplexer]
+ MEM[Unified Memory]
+ BUS[8-bit Data Bus]
+ AC[Accumulator]
+ ALU[Arithmetic Logic Unit]
+ CTRL[Controller FSM]
+
+ PC --> MUX
+ IR --> MUX
+ MUX --> MEM
+ MEM --> BUS
+ BUS --> IR
+ BUS --> ALU
+ AC --> ALU
+ ALU --> AC
+
+ CTRL --> PC
+ CTRL --> IR
+ CTRL --> MEM
+ CTRL --> AC
+ CTRL --> MUX
+```
+
+The controller coordinates memory access, register updates, ALU operations, and program-counter changes over multiple clock cycles.
+
+### Vivado Elaborated RTL Schematic
+
+
+
+
+
+
+
+ Elaborated RTL schematic showing the program counter, instruction register,
+ address multiplexer, accumulator, ALU, unified memory, and FSM controller.
+
+
+
+
+Detailed synthesized netlist
+
+
+
+
+
+
+
+---
+
+## Instruction Set Architecture
+
+### Instruction Format
+
+Each instruction occupies one byte:
+
+```text
++-------------+----------------+
+| Opcode (3) | Operand (5) |
++-------------+----------------+
+ 7 5 4 0
+```
+
+- **Opcode** identifies the operation.
+- **Operand** represents a memory address or jump destination.
+
+### Instruction Set
+
+| Opcode | Mnemonic | Operation |
+|---|---|---|
+| `000` | `HLT` | Stop processor execution |
+| `001` | `SKZ` | Skip the next instruction when `AC == 0` |
+| `010` | `ADD addr` | `AC ← AC + MEM[addr]` |
+| `011` | `AND addr` | `AC ← AC AND MEM[addr]` |
+| `100` | `XOR addr` | `AC ← AC XOR MEM[addr]` |
+| `101` | `LDA addr` | `AC ← MEM[addr]` |
+| `110` | `STO addr` | `MEM[addr] ← AC` |
+| `111` | `JMP addr` | `PC ← addr` |
+
+---
+
+## Multi-Cycle Execution
+
+Instructions are executed over multiple clock cycles so that datapath resources can be reused. A typical instruction passes through some or all of the following stages:
+
+```text
+Instruction Address
+ │
+ ▼
+Instruction Fetch
+ │
+ ▼
+Instruction Load
+ │
+ ▼
+Decode / Idle
+ │
+ ▼
+Operand Address
+ │
+ ▼
+Operand Fetch
+ │
+ ▼
+Execute
+ │
+ ▼
+Write Back
+```
+
+The controller selects only the states required by the current instruction.
+
+### Controller States
+
+| State | Purpose |
+|---|---|
+| `INST_ADDR` | Select the instruction address |
+| `INST_FETCH` | Read instruction memory |
+| `INST_LOAD` | Load the instruction register |
+| `IDLE` | Decode the current instruction |
+| `OP_ADDR` | Select the operand address |
+| `OP_FETCH` | Read the operand |
+| `ALU_OP` | Execute an arithmetic, logical, or control operation |
+| `STORE` | Write a result to memory |
+
+### Control Signals
+
+| Signal | Description |
+|---|---|
+| `rd` | Memory read enable |
+| `wr` | Memory write enable |
+| `ld_ir` | Load the instruction register |
+| `ld_ac` | Load the accumulator |
+| `ld_pc` | Load the program counter |
+| `inc_pc` | Increment the program counter |
+| `sel` | Select the memory address source |
+| `data_e` | Enable data-bus output |
+
+---
+
+## RTL Modules
+
+| Module | Responsibility |
+|---|---|
+| `CPU.v` | Top-level processor and module integration |
+| `Controller.v` | FSM sequencing and control-signal generation |
+| `ALU.v` | Arithmetic and logical operations |
+| `Memory.v` | Unified instruction and data memory |
+| `PC.v` | Program counter |
+| `IR.v` | Instruction register |
+| `AC.v` | Accumulator register |
+
+### `CPU.v`
+
+The top-level module connects the datapath and controller. It selects memory addresses, routes the shared data bus, forwards control signals, and coordinates complete instruction execution.
+
+### `Controller.v`
+
+The controller sequences instruction states and generates the signals required for memory access, register loading, ALU operation, and program-counter updates.
+
+### `ALU.v`
+
+The ALU supports:
+
+- addition;
+- bitwise AND;
+- bitwise XOR;
+- accumulator loading.
+
+It also produces the zero condition used by `SKZ`.
+
+### `Memory.v`
+
+The unified memory stores both instructions and data.
+
+- 32 addressable locations
+- 8-bit data width
+- asynchronous read
+- synchronous write
+
+### `PC.v`
+
+The program counter stores the address of the next instruction and supports:
+
+- increment;
+- loading a jump destination;
+- synchronous reset.
+
+### `IR.v`
+
+The instruction register holds the current instruction so that its opcode and operand remain stable during execution.
+
+### `AC.v`
+
+The accumulator is the processor's only general-purpose register. It can load an ALU result, retain its current value, or reset synchronously.
+
# Simple RISC Processor in Verilog
A compact, accumulator-based RISC processor implemented in Verilog HDL. The design demonstrates the core principles of CPU organization, including instruction fetch, instruction decode, arithmetic and logic execution, memory access, program-counter control, and finite-state-machine-based control.
@@ -123,6 +391,24 @@ HLT ; Stop execution
## Repository Structure
```text
+Verilog-Simple-Risc-Processor/
+├── src/
+│ ├── CPU.v
+│ ├── Controller.v
+│ ├── ALU.v
+│ ├── Memory.v
+│ ├── PC.v
+│ ├── IR.v
+│ └── AC.v
+├── testbench/
+│ ├── test_001/
+│ ├── test_002/
+│ ├── ...
+│ └── test_010/
+├── docs/
+│ └── images/
+├── run_tests.py
+├── LICENSE
.
├── src/
│ ├── AC.v
@@ -177,6 +463,321 @@ HLT ; Stop execution
---
+## Verification
+
+The project uses two complementary verification levels:
+
+1. **Module-level verification** checks individual RTL components.
+2. **CPU integration verification** checks complete program execution and interactions among processor components.
+
+Tests are compiled with **Icarus Verilog**, executed with `vvp`, and evaluated against expected output.
+
+```text
+RTL Source
+ │
+ ▼
+Compile with iverilog
+ │
+ ▼
+Simulate with vvp
+ │
+ ▼
+Compare Output
+ │
+ ▼
+PASS / FAIL
+```
+
+### Test Coverage
+
+| Test | Description |
+|---|---|
+| `test_001` | Program-counter verification |
+| `test_002` | Memory-module verification |
+| `test_003` | Instruction-register verification |
+| `test_004` | Accumulator verification |
+| `test_005` | ALU arithmetic and logical operations |
+| `test_006` | Controller FSM verification |
+| `test_007` | Datapath signal verification |
+| `test_008` | CPU instruction execution |
+| `test_009` | CPU integration test |
+| `test_010` | Waveform-oriented CPU integration |
+
+All eight instructions are exercised by the integration tests:
+
+| Instruction | Verified |
+|---|:---:|
+| `HLT` | ✓ |
+| `SKZ` | ✓ |
+| `ADD` | ✓ |
+| `AND` | ✓ |
+| `XOR` | ✓ |
+| `LDA` | ✓ |
+| `STO` | ✓ |
+| `JMP` | ✓ |
+
+### Behavioral Simulation Waveform
+
+
+
+
+
+
+
+ Multi-cycle instruction execution showing datapath activity, controller
+ sequencing, memory access, conditional skip, jump, and halt behavior.
+
+
+
+---
+
+## Running the Project
+
+### Prerequisites
+
+Install:
+
+- Python 3
+- Icarus Verilog
+- GTKWave for waveform inspection
+
+### Run All Tests
+
+```bash
+python3 run_tests.py --src src --testbench testbench --sim icarus
+```
+
+### Run a Specific Test
+
+```bash
+python3 run_tests.py \
+ --src src \
+ --testbench testbench \
+ --sim icarus \
+ --filter test_010
+```
+
+Replace `test_010` with the required test name.
+
+### Expected Output
+
+```text
+Running test_001 ... PASS
+Running test_002 ... PASS
+...
+Running test_010 ... PASS
+
+Summary
+
+Passed : 10
+Failed : 0
+Skipped: 0
+```
+
+---
+
+## Waveform Debugging
+
+The waveform-oriented integration test produces a Value Change Dump (`.vcd`) file.
+
+Open it with GTKWave:
+
+```bash
+gtkwave CPU_tb_wave.vcd
+```
+
+Useful signals include:
+
+- program counter;
+- instruction register;
+- accumulator;
+- ALU output;
+- memory address and data;
+- controller state;
+- internal data bus;
+- zero flag;
+- control signals.
+
+A recommended debugging cycle is:
+
+1. Run the relevant test.
+2. Review the simulation output.
+3. Inspect the generated waveform.
+4. Locate incorrect datapath or controller behavior.
+5. Update the RTL.
+6. Run the full regression suite.
+
+---
+
+### Vivado Synthesis
+
+### Synthesized Design
+
+
+
+
+
+
+
+ Synthesized processor schematic generated by Vivado, showing the
+ main processor modules and FPGA-specific clock and I/O resources.
+
+
+
+### Detailed Synthesized Netlist
+
+
+View detailed synthesized logic
+
+
+
+
+
+
+
+ Detailed gate-level view of the synthesized processor logic.
+ This diagram illustrates how the RTL design is transformed into
+ registers, multiplexers, combinational logic, and control circuitry.
+
+
+
+
+
+---
+
+## Design Principles
+
+The implementation intentionally favors clarity over performance:
+
+- one hardware component per source file;
+- explicit separation of datapath and control logic;
+- a single accumulator instead of a register file;
+- multi-cycle execution instead of pipelining;
+- a compact instruction set;
+- unified program and data memory;
+- consistent signal naming;
+- synthesizable Verilog constructs.
+
+These choices make the project suitable for introductory computer architecture courses, digital-design laboratories, and architectural experiments.
+
+---
+
+## Current Limitations
+
+The following features are intentionally omitted:
+
+| Feature | Status |
+|---|:---:|
+| General-purpose register file | ✗ |
+| Immediate instructions | ✗ |
+| Pipeline | ✗ |
+| Hazard detection | ✗ |
+| Branch prediction | ✗ |
+| Interrupt support | ✗ |
+| Cache memory | ✗ |
+| Separate instruction and data memory | ✗ |
+| Memory-mapped I/O | ✗ |
+| Exceptions | ✗ |
+
+---
+
+## Future Work
+
+Possible extensions include:
+
+### Instruction Set
+
+- `SUB` and `OR`
+- shift operations
+- compare instructions
+- immediate operands
+- additional conditional branches
+
+### Datapath and Architecture
+
+- register file
+- barrel shifter
+- status register and additional flags
+- Harvard memory organization
+- five-stage pipeline
+- forwarding and hazard detection
+- pipeline flushing
+- interrupt controller
+
+### Verification
+
+- functional coverage
+- constrained-random testing
+- SystemVerilog testbenches
+- assertion-based verification
+- continuous integration with GitHub Actions
+
+---
+
+## Educational Outcomes
+
+The project provides practical experience with:
+
+- register-transfer-level design;
+- modular hardware development;
+- datapath organization;
+- finite-state-machine control;
+- instruction decoding and execution;
+- ALU, memory, and program-counter design;
+- multi-cycle processor control;
+- hardware simulation and verification;
+- waveform analysis.
+
+---
+
+## Contributing
+
+Contributions are welcome, including new instructions, additional testbenches, documentation improvements, RTL optimizations, and bug fixes.
+
+1. Fork the repository.
+2. Create a feature branch.
+3. Commit your changes.
+4. Confirm that all regression tests pass.
+5. Open a pull request.
+
+---
+
+## Project Status
+
+**Current version:** `1.1`
+
+The project is functionally stable and suitable for educational use. The current release includes the modular RTL implementation, multi-cycle controller, complete original instruction set, automated regression tests, CPU integration tests, waveform debugging, and project documentation.
+
+---
+
+## References
+
+- M. Morris Mano -*Computer System Architecture*
+- David A. Patterson and John L. Hennessy -*Computer Organization and Design*
+- Stephen Brown and Zvonko Vranesic -*Fundamentals of Digital Logic with Verilog Design*
+- IEEE Standard for the Verilog Hardware Description Language
+
+---
+
+## Authors
+
+- **VinhTechiee** -https://github.com/VinhTechiee
+- **ladonna-2511** -https://github.com/ladonna-2511
+- **lunaz27** -https://github.com/lunaz27
+
+---
+
+## Acknowledgements
+
+This project was developed as part of undergraduate coursework in digital logic and computer architecture.
+
+Special thanks to the instructors and teaching assistants whose lectures and laboratory exercises inspired this educational processor.
## Modules
### `PC.v` - Program Counter
@@ -395,4 +996,8 @@ This processor is designed as an educational RTL project and intentionally remai
## License
-No license file is currently included in this repository. Add a license before distributing, modifying, or reusing the code in public or commercial projects.
+This project is released under the **MIT License**. See the `LICENSE` file for details.
+
+---
+
+> *"The best way to understand a processor is to build one."*
diff --git a/docs/images/cpu_behavioral_waveform.png b/docs/images/cpu_behavioral_waveform.png
new file mode 100644
index 0000000..39597b0
Binary files /dev/null and b/docs/images/cpu_behavioral_waveform.png differ
diff --git a/docs/images/detailed_synthesized_netlist.png b/docs/images/detailed_synthesized_netlist.png
new file mode 100644
index 0000000..8c744a5
Binary files /dev/null and b/docs/images/detailed_synthesized_netlist.png differ
diff --git a/docs/images/rtl_elaborated_schematic.png b/docs/images/rtl_elaborated_schematic.png
new file mode 100644
index 0000000..5d9be8d
Binary files /dev/null and b/docs/images/rtl_elaborated_schematic.png differ
diff --git a/docs/images/synthesized_schematic.png b/docs/images/synthesized_schematic.png
new file mode 100644
index 0000000..2c24780
Binary files /dev/null and b/docs/images/synthesized_schematic.png differ
diff --git a/report/riscReport.pdf b/report/riscReport.pdf
index a9181f3..3962b40 100644
Binary files a/report/riscReport.pdf and b/report/riscReport.pdf differ
diff --git a/testbench/test_010/CPU_tb_wave.v b/testbench/test_010/CPU_tb_wave.v
index 5a40e36..3ee67a9 100644
--- a/testbench/test_010/CPU_tb_wave.v
+++ b/testbench/test_010/CPU_tb_wave.v
@@ -4,14 +4,12 @@
// CPU_tb.v -- Waveform-heavy CPU integration testbench
//
// Purpose:
-// This testbench is designed for viewing a rich waveform, not
-// only for checking HLT. It exposes many internal CPU signals
-// as top-level waveform aliases so they are easy to add/view.
+// This testbench verifies all original CPU instructions and
+// generates a rich waveform for GTKWave.
//
// Waveform outputs:
-// - CPU_tb_wave.vcd : standard VCD waveform
-// - CPU_tb_wave.shm, optional : Cadence/Xcelium SHM database
-// compile with +define+USE_SHM
+// - CPU_tb_wave.vcd
+// - CPU_tb_wave.shm, optional with +define+USE_SHM
//
// Instruction coverage:
// HLT, SKZ, ADD, AND, XOR, LDA, STO, JMP
@@ -39,6 +37,10 @@
module CPU_tb;
+ // Set to 1 to print one trace row per clock cycle.
+ // Keep at 0 for golden-output regression testing.
+ localparam VERBOSE = 1'b0;
+
// ------------------------------------------------------------
// Testbench signals
// ------------------------------------------------------------
@@ -88,73 +90,96 @@ module CPU_tb;
localparam [2:0] STORE = 3'd7;
// ------------------------------------------------------------
- // Waveform aliases: top-level names are easier to inspect
+ // Waveform aliases
+ // ------------------------------------------------------------
+ wire [4:0] wave_pc = dut.u_pc.pc_out;
+ wire [2:0] wave_state = dut.u_controller.state;
+ wire wave_halted_latch = dut.u_controller.halted;
+
+ wire [7:0] wave_ir_reg = dut.u_ir.ir_reg;
+ wire [2:0] wave_opcode = dut.opcode;
+ wire [4:0] wave_operand = dut.operand;
+
+ wire [4:0] wave_mem_addr = dut.mem_addr;
+ wire [7:0] wave_data_bus = dut.data_bus;
+ wire [7:0] wave_memory_out = dut.u_memory.data_out;
+
+ wire [7:0] wave_ac = dut.u_ac.ac_out;
+ wire [7:0] wave_alu_out = dut.alu_out;
+ wire wave_zero = dut.zero;
+
+ wire wave_sel = dut.sel;
+ wire wave_rd = dut.rd;
+ wire wave_ld_ir = dut.ld_ir;
+ wire wave_inc_pc = dut.inc_pc;
+ wire wave_ld_ac = dut.ld_ac;
+ wire wave_ld_pc = dut.ld_pc;
+ wire wave_wr = dut.wr;
+ wire wave_data_e = dut.data_e;
+
+ // ------------------------------------------------------------
+ // Derived waveform markers
+ // ------------------------------------------------------------
+ wire wave_is_fetch_phase =
+ (wave_state == INST_ADDR) ||
+ (wave_state == INST_FETCH) ||
+ (wave_state == INST_LOAD) ||
+ (wave_state == IDLE);
+
+ wire wave_is_operand_phase =
+ (wave_state == OP_ADDR) ||
+ (wave_state == OP_FETCH);
+
+ wire wave_is_execute_phase =
+ (wave_state == ALU_OP);
+
+ wire wave_is_store_phase =
+ (wave_state == STORE);
+
+ wire wave_aluop =
+ (wave_opcode == ADD) ||
+ (wave_opcode == AND) ||
+ (wave_opcode == XOR) ||
+ (wave_opcode == LDA);
+
+ wire wave_skip_taken =
+ (wave_state == ALU_OP) &&
+ (wave_opcode == SKZ) &&
+ wave_zero;
+
+ wire wave_jump_taken =
+ ((wave_state == ALU_OP) ||
+ (wave_state == STORE)) &&
+ (wave_opcode == JMP) &&
+ wave_ld_pc;
+
+ wire wave_store_taken =
+ (wave_state == STORE) &&
+ (wave_opcode == STO) &&
+ wave_wr;
+
+ wire wave_ac_load_event =
+ (wave_state == STORE) &&
+ wave_ld_ac;
+
+ wire wave_mem_read_event =
+ wave_rd && !wave_wr;
+
+ wire wave_mem_write_event =
+ wave_wr && !wave_rd;
+
+ wire wave_exec_hlt = (wave_opcode == HLT);
+ wire wave_exec_skz = (wave_opcode == SKZ);
+ wire wave_exec_add = (wave_opcode == ADD);
+ wire wave_exec_and = (wave_opcode == AND);
+ wire wave_exec_xor = (wave_opcode == XOR);
+ wire wave_exec_lda = (wave_opcode == LDA);
+ wire wave_exec_sto = (wave_opcode == STO);
+ wire wave_exec_jmp = (wave_opcode == JMP);
+
+ // ------------------------------------------------------------
+ // Memory aliases
// ------------------------------------------------------------
- wire [4:0] wave_pc = dut.u_pc.pc_out;
- wire [2:0] wave_state = dut.u_controller.state;
- wire wave_halted_latch= dut.u_controller.halted;
-
- wire [7:0] wave_ir_reg = dut.u_ir.ir_reg;
- wire [2:0] wave_opcode = dut.opcode;
- wire [4:0] wave_operand = dut.operand;
-
- wire [4:0] wave_mem_addr = dut.mem_addr;
- wire [7:0] wave_data_bus = dut.data_bus;
- wire [7:0] wave_memory_out = dut.u_memory.data_out;
-
- wire [7:0] wave_ac = dut.u_ac.ac_out;
- wire [7:0] wave_alu_out = dut.alu_out;
- wire wave_zero = dut.zero;
-
- wire wave_sel = dut.sel;
- wire wave_rd = dut.rd;
- wire wave_ld_ir = dut.ld_ir;
- wire wave_inc_pc = dut.inc_pc;
- wire wave_ld_ac = dut.ld_ac;
- wire wave_ld_pc = dut.ld_pc;
- wire wave_wr = dut.wr;
- wire wave_data_e = dut.data_e;
-
- // Derived waveform markers.
- wire wave_is_fetch_phase = (wave_state == INST_ADDR) ||
- (wave_state == INST_FETCH) ||
- (wave_state == INST_LOAD) ||
- (wave_state == IDLE);
- wire wave_is_operand_phase = (wave_state == OP_ADDR) ||
- (wave_state == OP_FETCH);
- wire wave_is_execute_phase = (wave_state == ALU_OP);
- wire wave_is_store_phase = (wave_state == STORE);
-
- wire wave_aluop = (wave_opcode == ADD) ||
- (wave_opcode == AND) ||
- (wave_opcode == XOR) ||
- (wave_opcode == LDA);
- wire wave_skip_taken = (wave_state == ALU_OP) &&
- (wave_opcode == SKZ) &&
- wave_zero;
- wire wave_jump_taken = ((wave_state == ALU_OP) ||
- (wave_state == STORE)) &&
- (wave_opcode == JMP) &&
- wave_ld_pc;
- wire wave_store_taken = (wave_state == STORE) &&
- (wave_opcode == STO) &&
- wave_wr;
- wire wave_ac_load_event = (wave_state == STORE) &&
- wave_ld_ac;
- wire wave_mem_read_event = wave_rd && !wave_wr;
- wire wave_mem_write_event = wave_wr && !wave_rd;
-
- wire wave_exec_hlt = (wave_opcode == HLT);
- wire wave_exec_skz = (wave_opcode == SKZ);
- wire wave_exec_add = (wave_opcode == ADD);
- wire wave_exec_and = (wave_opcode == AND);
- wire wave_exec_xor = (wave_opcode == XOR);
- wire wave_exec_lda = (wave_opcode == LDA);
- wire wave_exec_sto = (wave_opcode == STO);
- wire wave_exec_jmp = (wave_opcode == JMP);
-
- // Memory aliases. Some VCD viewers do not show Verilog memories
- // conveniently, so each address is mirrored as a top-level wire.
wire [7:0] mem_00 = dut.u_memory.mem_cells[0];
wire [7:0] mem_01 = dut.u_memory.mem_cells[1];
wire [7:0] mem_02 = dut.u_memory.mem_cells[2];
@@ -188,7 +213,9 @@ module CPU_tb;
wire [7:0] mem_30 = dut.u_memory.mem_cells[30];
wire [7:0] mem_31 = dut.u_memory.mem_cells[31];
- // ASCII labels. In many waveform viewers these appear as readable text.
+ // ------------------------------------------------------------
+ // Readable waveform labels
+ // ------------------------------------------------------------
reg [8*12:1] wave_state_name;
reg [8*5 :1] wave_opcode_name;
reg [8*40:1] wave_program_line;
@@ -224,47 +251,125 @@ module CPU_tb;
always @(*) begin
case (wave_pc)
- 5'd0: wave_program_line = "00 LDA 24 AC<-MEM[24]=5 ";
- 5'd1: wave_program_line = "01 ADD 25 AC<-AC+MEM[25]=8 ";
- 5'd2: wave_program_line = "02 STO 26 MEM[26]<-AC=8 ";
- 5'd3: wave_program_line = "03 XOR 24 AC<-8^5=13 ";
- 5'd4: wave_program_line = "04 AND 25 AC<-13&3=1 ";
- 5'd5: wave_program_line = "05 STO 27 MEM[27]<-1 ";
- 5'd6: wave_program_line = "06 LDA 28 AC<-0 ";
- 5'd7: wave_program_line = "07 SKZ zero, skip next ";
- 5'd8: wave_program_line = "08 STO 29 MUST BE SKIPPED ";
- 5'd9: wave_program_line = "09 LDA 25 AC<-3 ";
- 5'd10: wave_program_line = "10 SKZ non-zero, no skip ";
- 5'd11: wave_program_line = "11 STO 30 MEM[30]<-3 ";
- 5'd12: wave_program_line = "12 JMP 14 jump to 14 ";
- 5'd13: wave_program_line = "13 STO 31 MUST BE SKIPPED ";
- 5'd14: wave_program_line = "14 LDA 26 AC<-MEM[26]=8 ";
- 5'd15: wave_program_line = "15 ADD 27 AC<-8+1=9 ";
- 5'd16: wave_program_line = "16 STO 23 MEM[23]<-9 ";
- 5'd17: wave_program_line = "17 HLT halt CPU ";
- default: wave_program_line = "outside programmed instruction area ";
+ 5'd0:
+ wave_program_line =
+ "00 LDA 24 AC<-MEM[24]=5 ";
+
+ 5'd1:
+ wave_program_line =
+ "01 ADD 25 AC<-AC+MEM[25]=8 ";
+
+ 5'd2:
+ wave_program_line =
+ "02 STO 26 MEM[26]<-AC=8 ";
+
+ 5'd3:
+ wave_program_line =
+ "03 XOR 24 AC<-8^5=13 ";
+
+ 5'd4:
+ wave_program_line =
+ "04 AND 25 AC<-13&3=1 ";
+
+ 5'd5:
+ wave_program_line =
+ "05 STO 27 MEM[27]<-1 ";
+
+ 5'd6:
+ wave_program_line =
+ "06 LDA 28 AC<-0 ";
+
+ 5'd7:
+ wave_program_line =
+ "07 SKZ zero, skip next ";
+
+ 5'd8:
+ wave_program_line =
+ "08 STO 29 MUST BE SKIPPED ";
+
+ 5'd9:
+ wave_program_line =
+ "09 LDA 25 AC<-3 ";
+
+ 5'd10:
+ wave_program_line =
+ "10 SKZ non-zero, no skip ";
+
+ 5'd11:
+ wave_program_line =
+ "11 STO 30 MEM[30]<-3 ";
+
+ 5'd12:
+ wave_program_line =
+ "12 JMP 14 jump to 14 ";
+
+ 5'd13:
+ wave_program_line =
+ "13 STO 31 MUST BE SKIPPED ";
+
+ 5'd14:
+ wave_program_line =
+ "14 LDA 26 AC<-MEM[26]=8 ";
+
+ 5'd15:
+ wave_program_line =
+ "15 ADD 27 AC<-8+1=9 ";
+
+ 5'd16:
+ wave_program_line =
+ "16 STO 23 MEM[23]<-9 ";
+
+ 5'd17:
+ wave_program_line =
+ "17 HLT halt CPU ";
+
+ default:
+ wave_program_line =
+ "outside programmed instruction area ";
endcase
end
always @(*) begin
- if (wave_skip_taken)
- wave_expected_action = "SKZ TAKEN: PC increments again ";
- else if ((wave_opcode == SKZ) && (wave_state == ALU_OP) && !wave_zero)
- wave_expected_action = "SKZ NOT TAKEN ";
- else if (wave_jump_taken)
- wave_expected_action = "JMP TAKEN: PC loads operand ";
- else if (wave_store_taken)
- wave_expected_action = "STO WRITE TO MEMORY ";
- else if (wave_ac_load_event)
- wave_expected_action = "AC LOAD FROM ALU ";
- else if (wave_mem_read_event)
- wave_expected_action = "MEMORY READ ";
- else if (wave_mem_write_event)
- wave_expected_action = "MEMORY WRITE ";
- else if (halt)
- wave_expected_action = "HALT ASSERTED ";
- else
- wave_expected_action = "normal CPU cycle ";
+ if (wave_skip_taken) begin
+ wave_expected_action =
+ "SKZ TAKEN: PC increments again ";
+ end
+ else if (
+ (wave_opcode == SKZ) &&
+ (wave_state == ALU_OP) &&
+ !wave_zero
+ ) begin
+ wave_expected_action =
+ "SKZ NOT TAKEN ";
+ end
+ else if (wave_jump_taken) begin
+ wave_expected_action =
+ "JMP TAKEN: PC loads operand ";
+ end
+ else if (wave_store_taken) begin
+ wave_expected_action =
+ "STO WRITE TO MEMORY ";
+ end
+ else if (wave_ac_load_event) begin
+ wave_expected_action =
+ "AC LOAD FROM ALU ";
+ end
+ else if (wave_mem_read_event) begin
+ wave_expected_action =
+ "MEMORY READ ";
+ end
+ else if (wave_mem_write_event) begin
+ wave_expected_action =
+ "MEMORY WRITE ";
+ end
+ else if (halt) begin
+ wave_expected_action =
+ "HALT ASSERTED ";
+ end
+ else begin
+ wave_expected_action =
+ "normal CPU cycle ";
+ end
end
// ------------------------------------------------------------
@@ -273,46 +378,65 @@ module CPU_tb;
initial begin
$timeformat(-9, 0, " ns", 10);
- // Standard waveform for GTKWave / many simulators.
$dumpfile("CPU_tb_wave.vcd");
$dumpvars(0, CPU_tb);
`ifdef USE_SHM
- // Cadence/Xcelium waveform database.
- // Example:
- // xrun +access+rwc +define+USE_SHM *.v
$shm_open("CPU_tb_wave.shm");
$shm_probe(CPU_tb, "ASCM");
`endif
end
// ------------------------------------------------------------
- // Helper tasks for checking results
+ // Helper tasks
// ------------------------------------------------------------
task check_mem;
input [4:0] addr;
input [7:0] expected;
+
begin
tests = tests + 1;
+
if (dut.u_memory.mem_cells[addr] !== expected) begin
errors = errors + 1;
- $display("FAIL: MEM[%0d] = 0x%02h, expected 0x%02h",
- addr, dut.u_memory.mem_cells[addr], expected);
- end else begin
- $display("PASS: MEM[%0d] = 0x%02h", addr, expected);
+
+ $display(
+ "FAIL: MEM[%0d] = 0x%02h, expected 0x%02h",
+ addr,
+ dut.u_memory.mem_cells[addr],
+ expected
+ );
+ end
+ else begin
+ $display(
+ "PASS: MEM[%0d] = 0x%02h",
+ addr,
+ expected
+ );
end
end
endtask
task check_ac;
input [7:0] expected;
+
begin
tests = tests + 1;
+
if (wave_ac !== expected) begin
errors = errors + 1;
- $display("FAIL: AC = 0x%02h, expected 0x%02h", wave_ac, expected);
- end else begin
- $display("PASS: AC = 0x%02h", expected);
+
+ $display(
+ "FAIL: AC = 0x%02h, expected 0x%02h",
+ wave_ac,
+ expected
+ );
+ end
+ else begin
+ $display(
+ "PASS: AC = 0x%02h",
+ expected
+ );
end
end
endtask
@@ -320,10 +444,16 @@ module CPU_tb;
task check_halt;
begin
tests = tests + 1;
+
if (halt !== 1'b1) begin
errors = errors + 1;
- $display("FAIL: halt = %b, expected 1", halt);
- end else begin
+
+ $display(
+ "FAIL: halt = %b, expected 1",
+ halt
+ );
+ end
+ else begin
$display("PASS: halt asserted");
end
end
@@ -331,13 +461,24 @@ module CPU_tb;
task check_pc;
input [4:0] expected;
+
begin
tests = tests + 1;
+
if (wave_pc !== expected) begin
errors = errors + 1;
- $display("FAIL: PC = %0d, expected %0d", wave_pc, expected);
- end else begin
- $display("PASS: PC = %0d", expected);
+
+ $display(
+ "FAIL: PC = %0d, expected %0d",
+ wave_pc,
+ expected
+ );
+ end
+ else begin
+ $display(
+ "PASS: PC = %0d",
+ expected
+ );
end
end
endtask
@@ -355,76 +496,99 @@ module CPU_tb;
dut.u_memory.mem_cells[i] = 8'h00;
end
- // Instruction encoding: {opcode[2:0], operand[4:0]}.
- dut.u_memory.mem_cells[0] = 8'hB8; // LDA 24
- dut.u_memory.mem_cells[1] = 8'h59; // ADD 25
- dut.u_memory.mem_cells[2] = 8'hDA; // STO 26
- dut.u_memory.mem_cells[3] = 8'h98; // XOR 24
- dut.u_memory.mem_cells[4] = 8'h79; // AND 25
- dut.u_memory.mem_cells[5] = 8'hDB; // STO 27
- dut.u_memory.mem_cells[6] = 8'hBC; // LDA 28
- dut.u_memory.mem_cells[7] = 8'h20; // SKZ
- dut.u_memory.mem_cells[8] = 8'hDD; // STO 29, skipped
- dut.u_memory.mem_cells[9] = 8'hB9; // LDA 25
- dut.u_memory.mem_cells[10] = 8'h20; // SKZ, not skipped
- dut.u_memory.mem_cells[11] = 8'hDE; // STO 30
- dut.u_memory.mem_cells[12] = 8'hEE; // JMP 14
- dut.u_memory.mem_cells[13] = 8'hDF; // STO 31, skipped
- dut.u_memory.mem_cells[14] = 8'hBA; // LDA 26
- dut.u_memory.mem_cells[15] = 8'h5B; // ADD 27
- dut.u_memory.mem_cells[16] = 8'hD7; // STO 23
- dut.u_memory.mem_cells[17] = 8'h00; // HLT
+ // Instruction encoding:
+ // {opcode[2:0], operand[4:0]}
+ dut.u_memory.mem_cells[0] = 8'hB8; // LDA 24
+ dut.u_memory.mem_cells[1] = 8'h59; // ADD 25
+ dut.u_memory.mem_cells[2] = 8'hDA; // STO 26
+ dut.u_memory.mem_cells[3] = 8'h98; // XOR 24
+ dut.u_memory.mem_cells[4] = 8'h79; // AND 25
+ dut.u_memory.mem_cells[5] = 8'hDB; // STO 27
+ dut.u_memory.mem_cells[6] = 8'hBC; // LDA 28
+ dut.u_memory.mem_cells[7] = 8'h20; // SKZ
+ dut.u_memory.mem_cells[8] = 8'hDD; // STO 29, skipped
+ dut.u_memory.mem_cells[9] = 8'hB9; // LDA 25
+ dut.u_memory.mem_cells[10] = 8'h20; // SKZ, not skipped
+ dut.u_memory.mem_cells[11] = 8'hDE; // STO 30
+ dut.u_memory.mem_cells[12] = 8'hEE; // JMP 14
+ dut.u_memory.mem_cells[13] = 8'hDF; // STO 31, skipped
+ dut.u_memory.mem_cells[14] = 8'hBA; // LDA 26
+ dut.u_memory.mem_cells[15] = 8'h5B; // ADD 27
+ dut.u_memory.mem_cells[16] = 8'hD7; // STO 23
+ dut.u_memory.mem_cells[17] = 8'h00; // HLT
// Data memory and sentinels.
- dut.u_memory.mem_cells[23] = 8'h00; // final result should be 9
+ dut.u_memory.mem_cells[23] = 8'h00;
dut.u_memory.mem_cells[24] = 8'd5;
dut.u_memory.mem_cells[25] = 8'd3;
- dut.u_memory.mem_cells[26] = 8'h00; // should become 8
- dut.u_memory.mem_cells[27] = 8'h00; // should become 1
+ dut.u_memory.mem_cells[26] = 8'h00;
+ dut.u_memory.mem_cells[27] = 8'h00;
dut.u_memory.mem_cells[28] = 8'd0;
- dut.u_memory.mem_cells[29] = 8'hAA; // should remain AA; SKZ skip proof
- dut.u_memory.mem_cells[30] = 8'h00; // should become 3
- dut.u_memory.mem_cells[31] = 8'hBB; // should remain BB; JMP skip proof
-
- $display("============================================================");
- $display("CPU WAVEFORM TEST START");
- $display("Open CPU_tb_wave.vcd or CPU_tb_wave.shm to view many signals.");
- $display("============================================================");
+ dut.u_memory.mem_cells[29] = 8'hAA;
+ dut.u_memory.mem_cells[30] = 8'h00;
+ dut.u_memory.mem_cells[31] = 8'hBB;
+
+ if (VERBOSE) begin
+ $display(
+ "============================================================"
+ );
+ $display("CPU WAVEFORM TEST START");
+ $display(
+ "Open CPU_tb_wave.vcd or CPU_tb_wave.shm to view many signals."
+ );
+ $display(
+ "============================================================"
+ );
+ end
- // Keep reset high for a few clocks so reset behavior is visible.
+ // Keep reset high for four clock edges.
repeat (4) @(posedge clk);
#1 rst = 1'b0;
end
// ------------------------------------------------------------
- // Console trace: one row per positive clock edge
+ // Optional console trace
// ------------------------------------------------------------
always @(posedge clk) begin
if (rst) begin
cycle_count <= 0;
- $display("t=%0t | RESET | PC=%02d state=%0d AC=0x%02h",
- $time, wave_pc, wave_state, wave_ac);
- end else begin
+
+ if (VERBOSE) begin
+ $display(
+ "t=%0t | RESET | PC=%02d state=%0d AC=0x%02h",
+ $time,
+ wave_pc,
+ wave_state,
+ wave_ac
+ );
+ end
+ end
+ else begin
cycle_count <= cycle_count + 1;
- $display("t=%0t | cyc=%03d | PC=%02d | state=%0d | op=%b | operand=%02d | addr=%02d | bus=0x%02h | AC=0x%02h | ALU=0x%02h | zero=%b | rd=%b wr=%b ld_ir=%b ld_ac=%b inc_pc=%b ld_pc=%b halt=%b",
- $time,
- cycle_count,
- wave_pc,
- wave_state,
- wave_opcode,
- wave_operand,
- wave_mem_addr,
- wave_data_bus,
- wave_ac,
- wave_alu_out,
- wave_zero,
- wave_rd,
- wave_wr,
- wave_ld_ir,
- wave_ld_ac,
- wave_inc_pc,
- wave_ld_pc,
- halt);
+
+ if (VERBOSE) begin
+ $display(
+ "t=%0t | cyc=%03d | PC=%02d | state=%0d | op=%b | operand=%02d | addr=%02d | bus=0x%02h | AC=0x%02h | ALU=0x%02h | zero=%b | rd=%b wr=%b ld_ir=%b ld_ac=%b inc_pc=%b ld_pc=%b halt=%b",
+ $time,
+ cycle_count,
+ wave_pc,
+ wave_state,
+ wave_opcode,
+ wave_operand,
+ wave_mem_addr,
+ wave_data_bus,
+ wave_ac,
+ wave_alu_out,
+ wave_zero,
+ wave_rd,
+ wave_wr,
+ wave_ld_ir,
+ wave_ld_ac,
+ wave_inc_pc,
+ wave_ld_pc,
+ halt
+ );
+ end
end
end
@@ -434,41 +598,62 @@ module CPU_tb;
initial begin
wait (halt === 1'b1);
- // Continue a little after halt so the waveform clearly shows
- // halt staying high and the controller holding its state.
+ // Keep a few cycles after halt visible in waveform.
repeat (10) @(posedge clk);
- $display("============================================================");
+ $display(
+ "============================================================"
+ );
$display("CPU FINAL CHECKS");
- $display("============================================================");
+ $display(
+ "============================================================"
+ );
check_halt();
- check_mem(26, 8'd8); // LDA + ADD + STO
- check_mem(27, 8'd1); // XOR + AND + STO
- check_mem(29, 8'hAA); // SKZ zero: STO 29 skipped
- check_mem(30, 8'd3); // SKZ non-zero: STO 30 executed
- check_mem(31, 8'hBB); // JMP: STO 31 skipped
- check_mem(23, 8'd9); // 8 + 1 = 9
+ check_mem(26, 8'd8);
+ check_mem(27, 8'd1);
+ check_mem(29, 8'hAA);
+ check_mem(30, 8'd3);
+ check_mem(31, 8'hBB);
+ check_mem(23, 8'd9);
check_ac(8'd9);
check_pc(5'd18);
- $display("============================================================");
+ $display(
+ "============================================================"
+ );
+
if (errors == 0) begin
- $display("CPU WAVEFORM TEST STATUS: PASS (%0d checks)", tests);
- end else begin
- $display("CPU WAVEFORM TEST STATUS: FAIL (%0d errors / %0d checks)", errors, tests);
+ $display(
+ "CPU WAVEFORM TEST STATUS: PASS (%0d checks)",
+ tests
+ );
+ end
+ else begin
+ $display(
+ "CPU WAVEFORM TEST STATUS: FAIL (%0d errors / %0d checks)",
+ errors,
+ tests
+ );
end
- $display("============================================================");
+
+ $display(
+ "============================================================"
+ );
$finish;
end
+ // ------------------------------------------------------------
+ // Timeout protection
+ // ------------------------------------------------------------
initial begin
#50000;
+
$display("FAIL: timeout. CPU did not halt.");
$finish;
end
-endmodule
+endmodule
\ No newline at end of file
diff --git a/testbench/test_010/expected.txt b/testbench/test_010/expected.txt
new file mode 100644
index 0000000..5ac726f
--- /dev/null
+++ b/testbench/test_010/expected.txt
@@ -0,0 +1,15 @@
+============================================================
+CPU FINAL CHECKS
+============================================================
+PASS: halt asserted
+PASS: MEM[26] = 0x08
+PASS: MEM[27] = 0x01
+PASS: MEM[29] = 0xaa
+PASS: MEM[30] = 0x03
+PASS: MEM[31] = 0xbb
+PASS: MEM[23] = 0x09
+PASS: AC = 0x09
+PASS: PC = 18
+============================================================
+CPU WAVEFORM TEST STATUS: PASS (9 checks)
+============================================================
\ No newline at end of file
diff --git a/testbench/test_011/CPU_all_opcode_tb.v b/testbench/test_011/CPU_all_opcode_tb.v
new file mode 100644
index 0000000..4e92f6e
--- /dev/null
+++ b/testbench/test_011/CPU_all_opcode_tb.v
@@ -0,0 +1,165 @@
+`timescale 1ns / 1ps
+
+// ============================================================
+// CPU_all_opcode_tb.v
+//
+// Compact regression test for all original CPU opcodes:
+//
+// 000 HLT
+// 001 SKZ
+// 010 ADD
+// 011 AND
+// 100 XOR
+// 101 LDA
+// 110 STO
+// 111 JMP
+//
+// This testbench prints only one final PASS or FAIL line.
+// ============================================================
+
+module CPU_all_opcode_tb;
+
+ reg clk;
+ reg rst;
+
+ wire halt;
+
+ integer cycles;
+ integer errors;
+ integer i;
+
+ // ------------------------------------------------------------
+ // Device under test
+ // ------------------------------------------------------------
+ CPU dut (
+ .clk (clk),
+ .rst (rst),
+ .halt(halt)
+ );
+
+ // ------------------------------------------------------------
+ // Clock: 10 ns period
+ // ------------------------------------------------------------
+ initial begin
+ clk = 1'b0;
+ end
+
+ always #5 clk = ~clk;
+
+ // ------------------------------------------------------------
+ // Program and data initialization
+ // ------------------------------------------------------------
+ initial begin
+ rst = 1'b1;
+ cycles = 0;
+ errors = 0;
+
+ // Clear all memory locations.
+ for (i = 0; i < 32; i = i + 1) begin
+ dut.u_memory.mem_cells[i] = 8'h00;
+ end
+
+ // ----------------------------------------------------------
+ // Program
+ //
+ // 0: LDA 20 AC = 0F
+ // 1: AND 21 AC = 03
+ // 2: XOR 22 AC = F3
+ // 3: ADD 23 AC = F8
+ // 4: STO 24 MEM[24] = F8
+ // 5: LDA 25 AC = 00
+ // 6: SKZ skip address 7
+ // 7: LDA 26 must not execute
+ // 8: JMP 10 skip address 9
+ // 9: LDA 27 must not execute
+ // 10: HLT
+ // ----------------------------------------------------------
+
+ dut.u_memory.mem_cells[0] = 8'hB4; // LDA 20
+ dut.u_memory.mem_cells[1] = 8'h75; // AND 21
+ dut.u_memory.mem_cells[2] = 8'h96; // XOR 22
+ dut.u_memory.mem_cells[3] = 8'h57; // ADD 23
+ dut.u_memory.mem_cells[4] = 8'hD8; // STO 24
+ dut.u_memory.mem_cells[5] = 8'hB9; // LDA 25
+ dut.u_memory.mem_cells[6] = 8'h20; // SKZ
+ dut.u_memory.mem_cells[7] = 8'hBA; // LDA 26, skipped
+ dut.u_memory.mem_cells[8] = 8'hEA; // JMP 10
+ dut.u_memory.mem_cells[9] = 8'hBB; // LDA 27, skipped
+ dut.u_memory.mem_cells[10] = 8'h00; // HLT
+
+ // ----------------------------------------------------------
+ // Data
+ // ----------------------------------------------------------
+ dut.u_memory.mem_cells[20] = 8'h0F;
+ dut.u_memory.mem_cells[21] = 8'h03;
+ dut.u_memory.mem_cells[22] = 8'hF0;
+ dut.u_memory.mem_cells[23] = 8'h05;
+ dut.u_memory.mem_cells[24] = 8'h00;
+ dut.u_memory.mem_cells[25] = 8'h00;
+ dut.u_memory.mem_cells[26] = 8'hAA;
+ dut.u_memory.mem_cells[27] = 8'hBB;
+
+ // Keep reset asserted for four rising edges.
+ repeat (4) @(posedge clk);
+ #1 rst = 1'b0;
+ end
+
+ // ------------------------------------------------------------
+ // Run CPU and perform final checks
+ // ------------------------------------------------------------
+ initial begin
+ wait (rst === 1'b0);
+
+ while ((halt !== 1'b1) && (cycles < 300)) begin
+ @(posedge clk);
+ cycles = cycles + 1;
+ end
+
+ // Allow final sequential updates to settle.
+ #1;
+
+ if (halt !== 1'b1) begin
+ errors = errors + 1;
+ end
+
+ // ADD, AND and XOR result stored by STO.
+ if (dut.u_memory.mem_cells[24] !== 8'hF8) begin
+ errors = errors + 1;
+ end
+
+ // LDA 26 must be skipped by SKZ.
+ // If it executed, AC would become AA.
+ if (dut.u_memory.mem_cells[26] !== 8'hAA) begin
+ errors = errors + 1;
+ end
+
+ // LDA 27 must be skipped by JMP.
+ if (dut.u_memory.mem_cells[27] !== 8'hBB) begin
+ errors = errors + 1;
+ end
+
+ // LDA 25 executes before SKZ, so final AC must remain zero.
+ if (dut.u_ac.ac_out !== 8'h00) begin
+ errors = errors + 1;
+ end
+
+ if (errors == 0) begin
+ $display("PASS: all original opcodes");
+ end
+ else begin
+ $display("FAIL: all original opcodes (%0d errors)", errors);
+ end
+
+ $finish;
+ end
+
+ // ------------------------------------------------------------
+ // Absolute timeout protection
+ // ------------------------------------------------------------
+ initial begin
+ #50000;
+ $display("FAIL: timeout");
+ $finish;
+ end
+
+endmodule
\ No newline at end of file
diff --git a/testbench/test_011/expected.txt b/testbench/test_011/expected.txt
new file mode 100644
index 0000000..4aaa3fe
--- /dev/null
+++ b/testbench/test_011/expected.txt
@@ -0,0 +1 @@
+PASS: all original opcodes
\ No newline at end of file