diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 65 | ||||
| -rw-r--r-- | config.json | 6 | ||||
| -rw-r--r-- | rtl/lisp_coproc.sv | 453 | ||||
| -rw-r--r-- | rtl/tb_lisp_coproc.sv | 611 | ||||
| -rw-r--r-- | vlsi/tb_chip_core.v | 4 |
6 files changed, 631 insertions, 510 deletions
| @@ -2,3 +2,5 @@ rtl/lisp_coproc_sim | |||
| 2 | *.vcd | 2 | *.vcd |
| 3 | *~ | 3 | *~ |
| 4 | #* | 4 | #* |
| 5 | runs/ | ||
| 6 | obj_dir/ | ||
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8b7bf3a --- /dev/null +++ b/Makefile | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | # ============================================================================== | ||
| 2 | # CONFIGURATION | ||
| 3 | # ============================================================================== | ||
| 4 | PROJECT_NAME = lisp_coproc | ||
| 5 | CONFIG_FILE = config.json | ||
| 6 | |||
| 7 | # Tool Paths | ||
| 8 | LIBRELANE = librelane | ||
| 9 | VERILATOR = verilator | ||
| 10 | |||
| 11 | # PDK Paths (Standard Volare location) | ||
| 12 | PDK_ROOT ?= $(HOME)/.ciel/ciel/sky130/versions/0fe599b2afb6708d281543108caf8310912f54af/ | ||
| 13 | PDK = sky130A | ||
| 14 | LIB_VERILOG = $(PDK_ROOT)/$(PDK)/libs.ref/sky130_fd_sc_hd/verilog/sky130_fd_sc_hd.v | ||
| 15 | PRIM_VERILOG = $(PDK_ROOT)/$(PDK)/libs.ref/sky130_fd_sc_hd/verilog/primitives.v | ||
| 16 | |||
| 17 | # Simulation Flags | ||
| 18 | VERILATOR_FLAGS = --binary -j 0 --timing --trace --top-module tb_$(PROJECT_NAME) \ | ||
| 19 | -Wno-fatal -Wno-style -Wno-lint | ||
| 20 | |||
| 21 | # ============================================================================== | ||
| 22 | # TARGETS | ||
| 23 | # ============================================================================== | ||
| 24 | .PHONY: all rtl harden gls clean | ||
| 25 | |||
| 26 | # 1. Default: Run the full pipeline (RTL -> GDS -> Check) | ||
| 27 | all: rtl harden gls | ||
| 28 | |||
| 29 | # 2. RTL Verification: Runs your Golden SystemVerilog Testbench | ||
| 30 | rtl: | ||
| 31 | @echo "\n=== [1/3] Running RTL Verification ===" | ||
| 32 | $(VERILATOR) $(VERILATOR_FLAGS) \ | ||
| 33 | -DGL_SIM=0 \ | ||
| 34 | rtl/tb_$(PROJECT_NAME).sv rtl/$(PROJECT_NAME).sv | ||
| 35 | ./obj_dir/Vtb_$(PROJECT_NAME) | ||
| 36 | @echo ">>> RTL Verification Passed <<<" | ||
| 37 | |||
| 38 | # 3. Hardening: Runs LibreLane (Synthesis, Place & Route) | ||
| 39 | # Note: Uses --run-tag to create a predictable folder name for the GLS step | ||
| 40 | harden: | ||
| 41 | @echo "\n=== [2/3] Running LibreLane Hardening ===" | ||
| 42 | $(LIBRELANE) --flow Classic \ | ||
| 43 | --run-tag automated_run \ | ||
| 44 | --overwrite \ | ||
| 45 | $(CONFIG_FILE) | ||
| 46 | @echo ">>> Hardening Complete <<<" | ||
| 47 | |||
| 48 | # 4. Gate-Level Simulation (GLS): Verifies the final GDSII netlist | ||
| 49 | # Finds the netlist generated by the 'harden' step | ||
| 50 | GL_NETLIST = runs/automated_run/final/nl/$(PROJECT_NAME).nl.v | ||
| 51 | |||
| 52 | gls: | ||
| 53 | @echo "\n=== [3/3] Running Gate-Level Simulation (GLS) ===" | ||
| 54 | $(VERILATOR) $(VERILATOR_FLAGS) \ | ||
| 55 | -DGL_SIM=1 -DFUNCTIONAL -DUNIT_DELAY=\#1 \ | ||
| 56 | -I$(PDK_ROOT)/$(PDK)/libs.ref/sky130_fd_sc_hd/verilog \ | ||
| 57 | rtl/tb_$(PROJECT_NAME).sv \ | ||
| 58 | $(GL_NETLIST) \ | ||
| 59 | $(LIB_VERILOG) $(PRIM_VERILOG) | ||
| 60 | ./obj_dir/Vtb_$(PROJECT_NAME) | ||
| 61 | @echo ">>> GLS Verification Passed <<<" | ||
| 62 | |||
| 63 | # Clean up build artifacts | ||
| 64 | clean: | ||
| 65 | rm -rf obj_dir runs/automated_run $(PROJECT_NAME).vcd | ||
diff --git a/config.json b/config.json new file mode 100644 index 0000000..0f2e115 --- /dev/null +++ b/config.json | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | { | ||
| 2 | "DESIGN_NAME": "lisp_coproc", | ||
| 3 | "VERILOG_FILES": ["dir::rtl/lisp_coproc.sv"], | ||
| 4 | "CLOCK_PERIOD": 100, | ||
| 5 | "CLOCK_PORT": "clk" | ||
| 6 | } | ||
diff --git a/rtl/lisp_coproc.sv b/rtl/lisp_coproc.sv index 6bd8574..7d1c8a8 100644 --- a/rtl/lisp_coproc.sv +++ b/rtl/lisp_coproc.sv | |||
| @@ -1,220 +1,251 @@ | |||
| 1 | `timescale 1ns/1ps // be consistent with testbench | ||
| 2 | |||
| 3 | module lisp_coproc ( | 1 | module lisp_coproc ( |
| 4 | input wire clk, | 2 | input wire clk, |
| 5 | input wire rst, | 3 | input wire rst, |
| 6 | input wire cs, | 4 | input wire cs, |
| 7 | input wire rw, // 0=Write, 1=Read | 5 | input wire rw, // 0=Write, 1=Read |
| 8 | input wire [2:0] addr, | 6 | input wire [2:0] addr, |
| 9 | input wire [7:0] data_in, | 7 | input wire [7:0] data_in, |
| 10 | output reg [7:0] data_out | 8 | output reg [7:0] data_out |
| 11 | ); | 9 | ); |
| 12 | |||
| 13 | // ======================================================================== | ||
| 14 | // 1. DATAPATH Signals & Storage | ||
| 15 | // ======================================================================== | ||
| 16 | |||
| 17 | // Registers | ||
| 18 | reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg; | ||
| 19 | reg [7:0] heap [0:15]; | ||
| 20 | |||
| 21 | // Bump Allocator: Uniform 4-bit register (0-15) | ||
| 22 | reg [3:0] bump_alloc; | ||
| 23 | |||
| 24 | // Heap Status: Sticky bit to track if we have wrapped around (Full) | ||
| 25 | reg heap_filled; | ||
| 26 | |||
| 27 | // Internal Flags (Transient for current OP) | ||
| 28 | reg flag_err_heap, flag_err_type, flag_carry, flag_zero; | ||
| 29 | |||
| 30 | // ALU Signals | ||
| 31 | wire [5:0] alu_val_a = arg1_reg[5:0]; | ||
| 32 | wire [5:0] alu_val_b = arg2_reg[5:0]; | ||
| 33 | wire [6:0] alu_sum = alu_val_a + alu_val_b; | ||
| 34 | wire alu_eq = (arg1_reg == arg2_reg); | ||
| 35 | |||
| 36 | // Type Checkers | ||
| 37 | wire is_cons_a = (arg1_reg[7:6] == 2'b11); | ||
| 38 | wire is_num_a = (arg1_reg[7:6] == 2'b10); | ||
| 39 | wire is_num_b = (arg2_reg[7:6] == 2'b10); | ||
| 40 | |||
| 41 | // Allocation Logic (Datapath Adder) | ||
| 42 | // We use a 5-bit wire to capture the carry out. | ||
| 43 | // If bump_alloc is 14 (1110) + 2 = 16 (10000). | ||
| 44 | // alloc_sum[4] (Carry) is 1. alloc_sum[3:0] is 0000. | ||
| 45 | wire [4:0] alloc_sum = {1'b0, bump_alloc} + 5'd2; | ||
| 46 | wire alloc_carry = alloc_sum[4]; | ||
| 47 | |||
| 48 | // ======================================================================== | ||
| 49 | // 2. FSM CONTROLLER | ||
| 50 | // ======================================================================== | ||
| 51 | |||
| 52 | parameter [4:0] RESET = 5'b00001; | ||
| 53 | parameter [4:0] IDLE = 5'b00010; | ||
| 54 | parameter [4:0] DECODE = 5'b00100; | ||
| 55 | parameter [4:0] EXECUTE = 5'b01000; | ||
| 56 | parameter [4:0] WRITEBACK = 5'b10000; | ||
| 57 | |||
| 58 | reg [4:0] state, next_state; | ||
| 59 | |||
| 60 | always @(posedge clk or posedge rst) begin | ||
| 61 | if (rst) state <= RESET; | ||
| 62 | else state <= next_state; | ||
| 63 | end | ||
| 64 | |||
| 65 | always @(*) begin | ||
| 66 | next_state = state; | ||
| 67 | case (state) | ||
| 68 | RESET: next_state = IDLE; | ||
| 69 | IDLE: if (cs && !rw && addr == 3'h0) next_state = DECODE; | ||
| 70 | DECODE: next_state = EXECUTE; | ||
| 71 | EXECUTE: next_state = WRITEBACK; | ||
| 72 | WRITEBACK: next_state = IDLE; | ||
| 73 | default: next_state = IDLE; | ||
| 74 | endcase | ||
| 75 | end | ||
| 76 | |||
| 77 | // ======================================================================== | ||
| 78 | // 3. SEQUENTIAL LOGIC | ||
| 79 | // ======================================================================== | ||
| 80 | |||
| 81 | integer i; | ||
| 82 | always @(posedge clk or posedge rst) begin | ||
| 83 | if (rst) begin | ||
| 84 | opcode_reg <= 8'h00; | ||
| 85 | arg1_reg <= 8'h00; | ||
| 86 | arg2_reg <= 8'h00; | ||
| 87 | result_reg <= 8'h00; | ||
| 88 | bump_alloc <= 4'h0; | ||
| 89 | heap_filled <= 1'b0; | ||
| 90 | |||
| 91 | flag_err_heap <= 1'b0; | ||
| 92 | flag_err_type <= 1'b0; | ||
| 93 | flag_carry <= 1'b0; | ||
| 94 | flag_zero <= 1'b0; | ||
| 95 | |||
| 96 | for (i=0; i<16; i=i+1) heap[i] <= 8'h00; | ||
| 97 | |||
| 98 | end else begin | ||
| 99 | |||
| 100 | // --- MMIO Writes --- | ||
| 101 | if (cs && !rw) begin | ||
| 102 | case (addr) | ||
| 103 | 3'h0: opcode_reg <= data_in; | ||
| 104 | 3'h1: arg1_reg <= data_in; | ||
| 105 | 3'h2: arg2_reg <= data_in; | ||
| 106 | 3'h3: result_reg <= data_in; | ||
| 107 | default: ; | ||
| 108 | endcase | ||
| 109 | end | ||
| 110 | |||
| 111 | // --- State Actions --- | ||
| 112 | case (state) | ||
| 113 | RESET: begin | ||
| 114 | bump_alloc <= 4'h0; | ||
| 115 | heap_filled <= 1'b0; | ||
| 116 | end | ||
| 117 | 10 | ||
| 118 | IDLE: begin | 11 | // ======================================================================== |
| 119 | if (cs && !rw && addr == 3'h0) begin | 12 | // 1. DATAPATH Signals & Storage |
| 120 | flag_err_heap <= 1'b0; | 13 | // ======================================================================== |
| 121 | flag_err_type <= 1'b0; | 14 | |
| 122 | flag_carry <= 1'b0; | 15 | // Registers |
| 123 | flag_zero <= 1'b0; | 16 | reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg; |
| 124 | end | 17 | reg [7:0] heap [0:15]; |
| 125 | end | 18 | |
| 19 | // Bump Allocator: Uniform 4-bit register (0-15) | ||
| 20 | reg [3:0] bump_alloc; | ||
| 21 | |||
| 22 | // Heap Status: Sticky bit to track if we have wrapped around (Full) | ||
| 23 | reg heap_filled; | ||
| 24 | |||
| 25 | // Internal Flags (Transient for current OP) | ||
| 26 | reg flag_err_heap, flag_err_type, flag_carry, flag_zero; | ||
| 27 | |||
| 28 | // ALU Signals | ||
| 29 | wire [5:0] alu_val_a = arg1_reg[5:0]; | ||
| 30 | wire [5:0] alu_val_b = arg2_reg[5:0]; | ||
| 31 | wire [6:0] alu_sum = alu_val_a + alu_val_b; | ||
| 32 | wire alu_eq = (arg1_reg == arg2_reg); | ||
| 33 | |||
| 34 | // Type Checkers | ||
| 35 | wire is_cons_a = (arg1_reg[7:6] == 2'b11); | ||
| 36 | wire is_num_a = (arg1_reg[7:6] == 2'b10); | ||
| 37 | wire is_num_b = (arg2_reg[7:6] == 2'b10); | ||
| 38 | |||
| 39 | // Allocation Logic (Datapath Adder) | ||
| 40 | // We use a 5-bit wire to capture the carry out. | ||
| 41 | // If bump_alloc is 14 (1110) + 2 = 16 (10000). | ||
| 42 | // alloc_sum[4] (Carry) is 1. alloc_sum[3:0] is 0000. | ||
| 43 | wire [4:0] alloc_sum = {1'b0, bump_alloc} + 5'd2; | ||
| 44 | wire alloc_carry = alloc_sum[4]; | ||
| 126 | 45 | ||
| 127 | EXECUTE: begin | 46 | // ======================================================================== |
| 128 | case (opcode_reg) | 47 | // 2. FSM CONTROLLER (Safe Binary Encoding) |
| 129 | 8'h01: begin // CONS | 48 | // ======================================================================== |
| 130 | if (heap_filled) begin | 49 | |
| 131 | // If sticky flag is set, we are full. Error. | 50 | // Explicit 3-bit encoding avoids optimization ambiguity |
| 132 | flag_err_heap <= 1'b1; | 51 | localparam [2:0] RESET = 3'd0; |
| 133 | end else begin | 52 | localparam [2:0] IDLE = 3'd1; |
| 134 | // Perform allocation | 53 | localparam [2:0] DECODE = 3'd2; |
| 135 | heap[bump_alloc] <= arg1_reg; | 54 | localparam [2:0] EXECUTE = 3'd3; |
| 136 | heap[bump_alloc + 1] <= arg2_reg; | 55 | localparam [2:0] WRITEBACK = 3'd4; |
| 137 | |||
| 138 | // Update pointer (wraps automatically due to 4-bit) | ||
| 139 | bump_alloc <= alloc_sum[3:0]; | ||
| 140 | |||
| 141 | // If we generated a carry (14->16), mark heap as filled | ||
| 142 | if (alloc_carry) heap_filled <= 1'b1; | ||
| 143 | end | ||
| 144 | end | ||
| 145 | 8'h02: begin // CAR | ||
| 146 | if (!is_cons_a) flag_err_type <= 1'b1; | ||
| 147 | end | ||
| 148 | 8'h03: begin // CDR | ||
| 149 | if (!is_cons_a) flag_err_type <= 1'b1; | ||
| 150 | end | ||
| 151 | 8'h05: begin // EQ | ||
| 152 | if (alu_eq) flag_zero <= 1'b1; | ||
| 153 | end | ||
| 154 | 8'h06: begin // ADD | ||
| 155 | if (!is_num_a || !is_num_b) begin | ||
| 156 | flag_err_type <= 1'b1; | ||
| 157 | end else begin | ||
| 158 | if (alu_sum[6]) flag_carry <= 1'b1; | ||
| 159 | if (alu_sum[5:0] == 6'd0) flag_zero <= 1'b1; | ||
| 160 | end | ||
| 161 | end | ||
| 162 | default: ; | ||
| 163 | endcase | ||
| 164 | end | ||
| 165 | 56 | ||
| 166 | WRITEBACK: begin | 57 | reg [2:0] state, next_state; |
| 167 | case (opcode_reg) | 58 | reg busy_bit; |
| 168 | 8'h01: begin // CONS | 59 | |
| 169 | if (!flag_err_heap) | 60 | // Sequential Logic |
| 170 | // Math trick: If bump_alloc wrapped to 0, | 61 | always @(posedge clk or posedge rst) begin |
| 171 | // 0 - 2 = 14 (1110 in 2's comp), which is the correct pointer. | 62 | if (rst) state <= RESET; |
| 172 | result_reg <= {2'b11, 2'b00, bump_alloc - 4'd2}; | 63 | else state <= next_state; |
| 173 | end | 64 | end |
| 174 | 8'h02: begin // CAR | 65 | |
| 175 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0]]; | 66 | // Combinational Next-State Logic |
| 176 | end | 67 | always @(*) begin |
| 177 | 8'h03: begin // CDR | 68 | // 1. Default assignments to prevent latches |
| 178 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0] + 1]; | 69 | next_state = IDLE; // Default to IDLE (Safe recovery) |
| 179 | end | 70 | busy_bit = 1'b1; // Default to BUSY |
| 180 | 8'h04: begin // ATOM | 71 | |
| 181 | result_reg <= is_cons_a ? 8'h00 : 8'h41; | 72 | case (state) |
| 182 | end | 73 | RESET: begin |
| 183 | 8'h05: begin // EQ | 74 | next_state = IDLE; |
| 184 | result_reg <= alu_eq ? 8'h41 : 8'h00; | 75 | busy_bit = 1'b1; |
| 185 | end | 76 | end |
| 186 | 8'h06: begin // ADD | 77 | |
| 187 | if (!flag_err_type) result_reg <= {2'b10, alu_sum[5:0]}; | 78 | IDLE: begin |
| 188 | end | 79 | busy_bit = 1'b0; // Not Busy |
| 189 | default: ; | 80 | // Transition Logic |
| 190 | endcase | 81 | if (cs && !rw && addr == 3'h0) |
| 191 | end | 82 | next_state = DECODE; |
| 192 | default: ; | 83 | else |
| 193 | endcase | 84 | next_state = IDLE; |
| 85 | end | ||
| 86 | |||
| 87 | DECODE: begin | ||
| 88 | next_state = EXECUTE; | ||
| 89 | busy_bit = 1'b1; | ||
| 90 | end | ||
| 91 | |||
| 92 | EXECUTE: begin | ||
| 93 | next_state = WRITEBACK; | ||
| 94 | busy_bit = 1'b1; | ||
| 95 | end | ||
| 96 | |||
| 97 | WRITEBACK: begin | ||
| 98 | next_state = IDLE; | ||
| 99 | busy_bit = 1'b1; | ||
| 100 | end | ||
| 101 | |||
| 102 | default: begin | ||
| 103 | next_state = IDLE; | ||
| 104 | busy_bit = 1'b1; | ||
| 194 | end | 105 | end |
| 195 | end | 106 | endcase |
| 196 | 107 | end | |
| 197 | // ======================================================================== | 108 | |
| 198 | // 4. OUTPUT LOGIC | 109 | // ======================================================================== |
| 199 | // ======================================================================== | 110 | // 3. SEQUENTIAL LOGIC |
| 200 | 111 | // ======================================================================== | |
| 201 | wire busy_bit = (state != IDLE); | 112 | |
| 202 | // Note: bit 1 is the transient error flag, not the internal sticky state | 113 | integer i; |
| 203 | wire [7:0] current_status = {3'b000, flag_zero, flag_carry, flag_err_type, flag_err_heap, busy_bit}; | 114 | always @(posedge clk or posedge rst) begin |
| 204 | 115 | if (rst) begin | |
| 205 | always @(*) begin | 116 | opcode_reg <= 8'h00; |
| 206 | if (cs && rw) begin | 117 | arg1_reg <= 8'h00; |
| 118 | arg2_reg <= 8'h00; | ||
| 119 | result_reg <= 8'h00; | ||
| 120 | bump_alloc <= 4'h0; | ||
| 121 | heap_filled <= 1'b0; | ||
| 122 | |||
| 123 | flag_err_heap <= 1'b0; | ||
| 124 | flag_err_type <= 1'b0; | ||
| 125 | flag_carry <= 1'b0; | ||
| 126 | flag_zero <= 1'b0; | ||
| 127 | |||
| 128 | for (i=0; i<16; i=i+1) heap[i] <= 8'h00; | ||
| 129 | |||
| 130 | end else begin | ||
| 131 | |||
| 132 | // --- MMIO Writes --- | ||
| 133 | if (cs && !rw) begin | ||
| 207 | case (addr) | 134 | case (addr) |
| 208 | 3'h0: data_out = opcode_reg; | 135 | 3'h0: opcode_reg <= data_in; |
| 209 | 3'h1: data_out = arg1_reg; | 136 | 3'h1: arg1_reg <= data_in; |
| 210 | 3'h2: data_out = arg2_reg; | 137 | 3'h2: arg2_reg <= data_in; |
| 211 | 3'h3: data_out = result_reg; | 138 | 3'h3: result_reg <= data_in; |
| 212 | 3'h4: data_out = current_status; | 139 | default: ; |
| 213 | default: data_out = 8'h00; | ||
| 214 | endcase | 140 | endcase |
| 215 | end else begin | 141 | end |
| 216 | data_out = 8'hZZ; | 142 | |
| 217 | end | 143 | // --- State Actions --- |
| 218 | end | 144 | case (state) |
| 145 | RESET: begin | ||
| 146 | bump_alloc <= 4'h0; | ||
| 147 | heap_filled <= 1'b0; | ||
| 148 | end | ||
| 149 | |||
| 150 | IDLE: begin | ||
| 151 | if (cs && !rw && addr == 3'h0) begin | ||
| 152 | flag_err_heap <= 1'b0; | ||
| 153 | flag_err_type <= 1'b0; | ||
| 154 | flag_carry <= 1'b0; | ||
| 155 | flag_zero <= 1'b0; | ||
| 156 | end | ||
| 157 | end | ||
| 158 | |||
| 159 | EXECUTE: begin | ||
| 160 | case (opcode_reg) | ||
| 161 | 8'h01: begin // CONS | ||
| 162 | if (heap_filled) begin | ||
| 163 | // If sticky flag is set, we are full. Error. | ||
| 164 | flag_err_heap <= 1'b1; | ||
| 165 | end else begin | ||
| 166 | // Perform allocation | ||
| 167 | heap[bump_alloc] <= arg1_reg; | ||
| 168 | heap[(bump_alloc + 1) % 16] <= arg2_reg; | ||
| 169 | |||
| 170 | // Update pointer (wraps automatically due to 4-bit) | ||
| 171 | bump_alloc <= alloc_sum[3:0]; | ||
| 172 | |||
| 173 | // If we generated a carry (14->16), mark heap as filled | ||
| 174 | if (alloc_carry) heap_filled <= 1'b1; | ||
| 175 | end | ||
| 176 | end | ||
| 177 | 8'h02: begin // CAR | ||
| 178 | if (!is_cons_a) flag_err_type <= 1'b1; | ||
| 179 | end | ||
| 180 | 8'h03: begin // CDR | ||
| 181 | if (!is_cons_a) flag_err_type <= 1'b1; | ||
| 182 | end | ||
| 183 | 8'h05: begin // EQ | ||
| 184 | if (alu_eq) flag_zero <= 1'b1; | ||
| 185 | end | ||
| 186 | 8'h06: begin // ADD | ||
| 187 | if (!is_num_a || !is_num_b) begin | ||
| 188 | flag_err_type <= 1'b1; | ||
| 189 | end else begin | ||
| 190 | if (alu_sum[6]) flag_carry <= 1'b1; | ||
| 191 | if (alu_sum[5:0] == 6'd0) flag_zero <= 1'b1; | ||
| 192 | end | ||
| 193 | end | ||
| 194 | default: ; | ||
| 195 | endcase | ||
| 196 | end | ||
| 197 | |||
| 198 | WRITEBACK: begin | ||
| 199 | case (opcode_reg) | ||
| 200 | 8'h01: begin // CONS | ||
| 201 | if (!flag_err_heap) | ||
| 202 | // Math trick: If bump_alloc wrapped to 0, | ||
| 203 | // 0 - 2 = 14 (1110 in 2's comp), which is the correct pointer. | ||
| 204 | result_reg <= {2'b11, 2'b00, bump_alloc - 4'd2}; | ||
| 205 | end | ||
| 206 | 8'h02: begin // CAR | ||
| 207 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0]]; | ||
| 208 | end | ||
| 209 | 8'h03: begin // CDR | ||
| 210 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0] + 1]; | ||
| 211 | end | ||
| 212 | 8'h04: begin // ATOM | ||
| 213 | result_reg <= is_cons_a ? 8'h00 : 8'h41; | ||
| 214 | end | ||
| 215 | 8'h05: begin // EQ | ||
| 216 | result_reg <= alu_eq ? 8'h41 : 8'h00; | ||
| 217 | end | ||
| 218 | 8'h06: begin // ADD | ||
| 219 | if (!flag_err_type) result_reg <= {2'b10, alu_sum[5:0]}; | ||
| 220 | end | ||
| 221 | default: ; | ||
| 222 | endcase | ||
| 223 | end | ||
| 224 | default: ; | ||
| 225 | endcase | ||
| 226 | end | ||
| 227 | end | ||
| 228 | |||
| 229 | // ======================================================================== | ||
| 230 | // 4. OUTPUT LOGIC | ||
| 231 | // ======================================================================== | ||
| 232 | |||
| 233 | // Status Register: [7:5]Rsrv, [4]Zero, [3]Carry, [2]Type, [1]Heap, [0]Busy | ||
| 234 | wire [7:0] current_status = {3'b000, flag_zero, flag_carry, flag_err_type, flag_err_heap, busy_bit}; | ||
| 235 | |||
| 236 | always @(*) begin | ||
| 237 | if (cs && rw) begin | ||
| 238 | case (addr) | ||
| 239 | 3'h0: data_out = opcode_reg; | ||
| 240 | 3'h1: data_out = arg1_reg; | ||
| 241 | 3'h2: data_out = arg2_reg; | ||
| 242 | 3'h3: data_out = result_reg; | ||
| 243 | 3'h4: data_out = current_status; | ||
| 244 | default: data_out = 8'h00; | ||
| 245 | endcase | ||
| 246 | end else begin | ||
| 247 | data_out = 8'h00; // Drive 0 instead of Z to satisfy internal logic checks | ||
| 248 | end | ||
| 249 | end | ||
| 219 | 250 | ||
| 220 | endmodule | 251 | endmodule |
diff --git a/rtl/tb_lisp_coproc.sv b/rtl/tb_lisp_coproc.sv index 08e3627..4e7b28d 100644 --- a/rtl/tb_lisp_coproc.sv +++ b/rtl/tb_lisp_coproc.sv | |||
| @@ -2,305 +2,322 @@ | |||
| 2 | 2 | ||
| 3 | module tb_lisp_coproc; | 3 | module tb_lisp_coproc; |
| 4 | 4 | ||
| 5 | // ======================================================================== | 5 | // ======================================================================== |
| 6 | // 1. CONFIGURATION & CONSTANTS | 6 | // 1. CONFIGURATION & CONSTANTS |
| 7 | // ======================================================================== | 7 | // ======================================================================== |
| 8 | 8 | ||
| 9 | // Opcodes | 9 | // Opcodes |
| 10 | localparam [7:0] OP_CONS = 8'h01; | 10 | localparam [7:0] OP_CONS = 8'h01; |
| 11 | localparam [7:0] OP_CAR = 8'h02; | 11 | localparam [7:0] OP_CAR = 8'h02; |
| 12 | localparam [7:0] OP_CDR = 8'h03; | 12 | localparam [7:0] OP_CDR = 8'h03; |
| 13 | localparam [7:0] OP_ATOM = 8'h04; | 13 | localparam [7:0] OP_ATOM = 8'h04; |
| 14 | localparam [7:0] OP_EQ = 8'h05; | 14 | localparam [7:0] OP_EQ = 8'h05; |
| 15 | localparam [7:0] OP_ADD = 8'h06; | 15 | localparam [7:0] OP_ADD = 8'h06; |
| 16 | 16 | ||
| 17 | // Tags | 17 | // Tags |
| 18 | localparam [1:0] TAG_NIL = 2'b00; | 18 | localparam [1:0] TAG_NIL = 2'b00; |
| 19 | localparam [1:0] TAG_ATOM = 2'b01; | 19 | localparam [1:0] TAG_ATOM = 2'b01; |
| 20 | localparam [1:0] TAG_NUM = 2'b10; | 20 | localparam [1:0] TAG_NUM = 2'b10; |
| 21 | localparam [1:0] TAG_CONS = 2'b11; | 21 | localparam [1:0] TAG_CONS = 2'b11; |
| 22 | 22 | ||
| 23 | // Standard Values for Testing | 23 | // Standard Values for Testing |
| 24 | localparam [7:0] VAL_NIL = {TAG_NIL, 6'h00}; | 24 | localparam [7:0] VAL_NIL = {TAG_NIL, 6'h00}; |
| 25 | localparam [7:0] VAL_TRUE = {TAG_ATOM, 6'h01}; // 'T' | 25 | localparam [7:0] VAL_TRUE = {TAG_ATOM, 6'h01}; // 'T' |
| 26 | localparam [7:0] VAL_A = {TAG_ATOM, 6'h0A}; | 26 | localparam [7:0] VAL_A = {TAG_ATOM, 6'h0A}; |
| 27 | localparam [7:0] VAL_B = {TAG_ATOM, 6'h0B}; | 27 | localparam [7:0] VAL_B = {TAG_ATOM, 6'h0B}; |
| 28 | localparam [7:0] VAL_C = {TAG_ATOM, 6'h0C}; | 28 | localparam [7:0] VAL_C = {TAG_ATOM, 6'h0C}; |
| 29 | 29 | ||
| 30 | // Testbench Signals | 30 | // Testbench Signals |
| 31 | reg clk, rst, cs, rw; | 31 | reg clk, rst, cs, rw; |
| 32 | reg [2:0] addr; | 32 | reg [2:0] addr; |
| 33 | reg [7:0] data_in; | 33 | reg [7:0] data_in; |
| 34 | wire [7:0] data_out; | 34 | wire [7:0] data_out; |
| 35 | 35 | ||
| 36 | // Verification Variables | 36 | // Verification Variables |
| 37 | reg [7:0] read_val; | 37 | reg [7:0] read_val; |
| 38 | reg [7:0] status_val; | 38 | reg [7:0] status_val; |
| 39 | reg [7:0] ptr_node3, ptr_node2, ptr_node1; | 39 | reg [7:0] ptr_node3, ptr_node2, ptr_node1; |
| 40 | integer errors = 0; | 40 | integer errors = 0; |
| 41 | integer i; | 41 | integer i; |
| 42 | 42 | ||
| 43 | // Instantiate DUT | 43 | // Instantiate DUT |
| 44 | lisp_coproc dut ( | 44 | lisp_coproc dut ( |
| 45 | .clk(clk), .rst(rst), .cs(cs), .rw(rw), | 45 | .clk(clk), .rst(rst), .cs(cs), .rw(rw), |
| 46 | .addr(addr), .data_in(data_in), .data_out(data_out) | 46 | .addr(addr), .data_in(data_in), .data_out(data_out) |
| 47 | ); | 47 | ); |
| 48 | 48 | ||
| 49 | // Clock Generation (100MHz) | 49 | // Clock Generation (10MHz) |
| 50 | initial begin | 50 | initial begin |
| 51 | clk = 0; | 51 | clk = 0; |
| 52 | forever #5 clk = ~clk; | 52 | forever #50 clk = ~clk; |
| 53 | end | 53 | end |
| 54 | 54 | ||
| 55 | // ======================================================================== | 55 | // ======================================================================== |
| 56 | // 2. HELPER TASKS | 56 | // 2. HELPER TASKS |
| 57 | // ======================================================================== | 57 | // ======================================================================== |
| 58 | 58 | ||
| 59 | task cpu_write(input [2:0] w_addr, input [7:0] w_data); | 59 | task cpu_write(input [2:0] w_addr, input [7:0] w_data); |
| 60 | begin | 60 | begin |
| 61 | @(posedge clk); | ||
| 62 | cs = 1; rw = 0; addr = w_addr; data_in = w_data; | ||
| 63 | @(posedge clk); | ||
| 64 | cs = 0; data_in = 8'h00; | ||
| 65 | end | ||
| 66 | endtask | ||
| 67 | |||
| 68 | // Read with auto-polling for BUSY flag | ||
| 69 | task cpu_exec_and_read(input [2:0] r_addr, output [7:0] r_data); | ||
| 70 | integer timeout; | ||
| 71 | begin | ||
| 72 | timeout = 0; | ||
| 73 | // Poll Status Bit 0 (BUSY) | ||
| 74 | do begin | ||
| 61 | @(posedge clk); | 75 | @(posedge clk); |
| 62 | cs = 1; rw = 0; addr = w_addr; data_in = w_data; | 76 | cs = 1; rw = 1; addr = 3'h4; // Status |
| 63 | @(posedge clk); | 77 | @(posedge clk); |
| 64 | cs = 0; data_in = 8'h00; | 78 | status_val = data_out; |
| 65 | end | ||
| 66 | endtask | ||
| 67 | |||
| 68 | // Read with auto-polling for BUSY flag | ||
| 69 | task cpu_exec_and_read(input [2:0] r_addr, output [7:0] r_data); | ||
| 70 | begin | ||
| 71 | // Poll Status Bit 0 (BUSY) | ||
| 72 | do begin | ||
| 73 | @(posedge clk); | ||
| 74 | cs = 1; rw = 1; addr = 3'h4; // Status | ||
| 75 | @(posedge clk); | ||
| 76 | status_val = data_out; | ||
| 77 | cs = 0; | ||
| 78 | end while (status_val[0] === 1'b1); | ||
| 79 | |||
| 80 | // Perform Read | ||
| 81 | @(posedge clk); | ||
| 82 | cs = 1; rw = 1; addr = r_addr; | ||
| 83 | @(posedge clk); | ||
| 84 | r_data = data_out; | ||
| 85 | cs = 0; | 79 | cs = 0; |
| 86 | end | 80 | |
| 87 | endtask | 81 | // Panic button: Break if stuck for 100 cycles |
| 88 | 82 | timeout = timeout + 1; | |
| 89 | task check(input [7:0] expected, input [7:0] actual, input string name); | 83 | if (timeout > 100) begin |
| 90 | if (expected !== actual) begin | 84 | $display("ERROR: Timed out polling BUSY bit! Status: %b", status_val); |
| 91 | $display("FAIL: %s | Exp: 0x%h, Got: 0x%h", name, expected, actual); | 85 | break; |
| 92 | errors = errors + 1; | 86 | end |
| 93 | end else begin | 87 | end while (status_val[0] === 1'b1); |
| 94 | $display("PASS: %s", name); | 88 | |
| 95 | end | 89 | // Perform Read |
| 96 | endtask | 90 | @(posedge clk); |
| 97 | 91 | cs = 1; | |
| 98 | task check_status(input bit exp_heap, input bit exp_type, input bit exp_carry, input bit exp_zero, input string name); | 92 | rw = 1; addr = r_addr; |
| 99 | // Status Reg: [7:5]Rsrv, [4]Zero, [3]Carry, [2]Type, [1]Heap, [0]Busy | 93 | @(posedge clk); |
| 100 | reg [7:0] expected_mask; | 94 | r_data = data_out; |
| 101 | expected_mask = {3'b000, exp_zero, exp_carry, exp_type, exp_heap, 1'b0}; | 95 | cs = 0; |
| 102 | 96 | end | |
| 103 | cpu_exec_and_read(3'h4, status_val); | 97 | endtask |
| 104 | // Mask out the busy bit for comparison as it should be 0 now | 98 | |
| 105 | if ((status_val & 8'hFE) !== expected_mask) begin | 99 | task check(input [7:0] expected, input [7:0] actual, input string name); |
| 106 | $display("FAIL: %s (Status) | Exp: %b, Got: %b", name, expected_mask, status_val); | 100 | if (expected !== actual) begin |
| 107 | errors = errors + 1; | 101 | $display("FAIL: %s | Exp: 0x%h, Got: 0x%h", name, expected, actual); |
| 108 | end else begin | 102 | errors = errors + 1; |
| 109 | $display("PASS: %s (Status)", name); | 103 | end else begin |
| 110 | end | 104 | $display("PASS: %s", name); |
| 111 | endtask | 105 | end |
| 112 | 106 | endtask | |
| 113 | // ======================================================================== | 107 | |
| 114 | // 3. MAIN TEST SCENARIOS | 108 | task check_status(input bit exp_heap, input bit exp_type, input bit exp_carry, input bit exp_zero, input string name); |
| 115 | // ======================================================================== | 109 | // Status Reg: [7:5]Rsrv, [4]Zero, [3]Carry, [2]Type, [1]Heap, [0]Busy |
| 116 | 110 | reg [7:0] expected_mask; | |
| 117 | initial begin | 111 | expected_mask = {3'b000, exp_zero, exp_carry, exp_type, exp_heap, 1'b0}; |
| 118 | $dumpfile("lisp_coproc_robust.vcd"); | 112 | |
| 119 | $dumpvars(0, tb_lisp_coproc); | 113 | cpu_exec_and_read(3'h4, status_val); |
| 120 | 114 | // Mask out the busy bit for comparison as it should be 0 now | |
| 121 | // --- Initialize --- | 115 | if ((status_val & 8'hFE) !== expected_mask) begin |
| 122 | rst = 1; cs = 0; rw = 0; addr = 0; data_in = 0; | 116 | $display("FAIL: %s (Status) | Exp: %b, Got: %b", name, expected_mask, status_val); |
| 123 | #20 rst = 0; #20; | 117 | errors = errors + 1; |
| 124 | $display("\n=== STARTING ROBUST VERIFICATION ===\n"); | 118 | end else begin |
| 125 | 119 | $display("PASS: %s (Status)", name); | |
| 126 | // -------------------------------------------------------- | 120 | end |
| 127 | // SCENARIO 1: ALU Boundary & Overflow | 121 | endtask |
| 128 | // -------------------------------------------------------- | 122 | |
| 129 | $display("--- Scenario 1: ALU Mathematics ---"); | 123 | // ======================================================================== |
| 130 | 124 | // 3. MAIN TEST SCENARIOS | |
| 131 | // 1.1 Simple Add: 10 + 15 = 25 | 125 | // ======================================================================== |
| 132 | cpu_write(3'h1, {TAG_NUM, 6'd10}); | 126 | |
| 133 | cpu_write(3'h2, {TAG_NUM, 6'd15}); | 127 | initial begin |
| 134 | cpu_write(3'h0, OP_ADD); | 128 | $dumpfile("lisp_coproc.vcd"); |
| 135 | cpu_exec_and_read(3'h3, read_val); | 129 | $dumpvars(0, tb_lisp_coproc); |
| 136 | check({TAG_NUM, 6'd25}, read_val, "Add 10+15"); | 130 | |
| 137 | check_status(0,0,0,0, "Add Normal Status"); | 131 | // --- Initialize --- |
| 138 | 132 | clk = 0; | |
| 139 | // 1.2 Zero Check: 0 + 0 = 0 (Should set Zero flag) | 133 | cs = 0; rw = 0; addr = 0; data_in = 0; |
| 140 | cpu_write(3'h1, {TAG_NUM, 6'd0}); | 134 | |
| 141 | cpu_write(3'h2, {TAG_NUM, 6'd0}); | 135 | // --- AGGRESSIVE RESET SEQUENCE --- |
| 142 | cpu_write(3'h0, OP_ADD); | 136 | rst = 1; // Assert Reset |
| 143 | cpu_exec_and_read(3'h3, read_val); | 137 | #500; // Hold for 50 cycles (allows X propagation to clear) |
| 144 | check({TAG_NUM, 6'd0}, read_val, "Add 0+0"); | 138 | rst = 0; // Release Reset |
| 145 | check_status(0,0,0,1, "Add Zero Status"); // Expect Zero=1 | 139 | #100; // Wait for logic to settle into IDLE |
| 146 | 140 | ||
| 147 | // 1.3 Overflow Check: 63 + 1 = 0 (Should set Carry flag) | 141 | $display("\n=== STARTING VERIFICATION ===\n"); |
| 148 | // Max 6-bit unsigned is 63. 63+1 wraps to 0. | 142 | |
| 149 | cpu_write(3'h1, {TAG_NUM, 6'd63}); | 143 | // -------------------------------------------------------- |
| 150 | cpu_write(3'h2, {TAG_NUM, 6'd1}); | 144 | // SCENARIO 1: ALU Boundary & Overflow |
| 151 | cpu_write(3'h0, OP_ADD); | 145 | // -------------------------------------------------------- |
| 152 | cpu_exec_and_read(3'h3, read_val); | 146 | $display("--- Scenario 1: ALU Mathematics ---"); |
| 153 | check({TAG_NUM, 6'd0}, read_val, "Add 63+1 (Wrap)"); | 147 | |
| 154 | check_status(0,0,1,1, "Add Overflow Status"); // Expect Carry=1, Zero=1 | 148 | // 1.1 Simple Add: 10 + 15 = 25 |
| 155 | 149 | cpu_write(3'h1, {TAG_NUM, 6'd10}); | |
| 156 | // -------------------------------------------------------- | 150 | cpu_write(3'h2, {TAG_NUM, 6'd15}); |
| 157 | // SCENARIO 2: Equality (EQ) Logic | 151 | cpu_write(3'h0, OP_ADD); |
| 158 | // -------------------------------------------------------- | 152 | cpu_exec_and_read(3'h3, read_val); |
| 159 | $display("\n--- Scenario 2: EQ Logic ---"); | 153 | check({TAG_NUM, 6'd25}, read_val, "Add 10+15"); |
| 160 | 154 | check_status(0,0,0,0, "Add Normal Status"); | |
| 161 | // 2.1 Atom Equality (True) | 155 | |
| 162 | cpu_write(3'h1, VAL_A); | 156 | // 1.2 Zero Check: 0 + 0 = 0 (Should set Zero flag) |
| 163 | cpu_write(3'h2, VAL_A); | 157 | cpu_write(3'h1, {TAG_NUM, 6'd0}); |
| 164 | cpu_write(3'h0, OP_EQ); | 158 | cpu_write(3'h2, {TAG_NUM, 6'd0}); |
| 165 | cpu_exec_and_read(3'h3, read_val); | 159 | cpu_write(3'h0, OP_ADD); |
| 166 | check(VAL_TRUE, read_val, "EQ(A, A)"); | 160 | cpu_exec_and_read(3'h3, read_val); |
| 167 | check_status(0,0,0,1, "EQ True Status"); // Zero flag used for equality? Spec says "ZERO (From ADD or EQ op)" | 161 | check({TAG_NUM, 6'd0}, read_val, "Add 0+0"); |
| 168 | 162 | check_status(0,0,0,1, "Add Zero Status"); // Expect Zero=1 | |
| 169 | // 2.2 Atom Inequality (False) | 163 | |
| 170 | cpu_write(3'h1, VAL_A); | 164 | // 1.3 Overflow Check: 63 + 1 = 0 (Should set Carry flag) |
| 171 | cpu_write(3'h2, VAL_B); | 165 | // Max 6-bit unsigned is 63. 63+1 wraps to 0. |
| 172 | cpu_write(3'h0, OP_EQ); | 166 | cpu_write(3'h1, {TAG_NUM, 6'd63}); |
| 173 | cpu_exec_and_read(3'h3, read_val); | 167 | cpu_write(3'h2, {TAG_NUM, 6'd1}); |
| 174 | check(VAL_NIL, read_val, "EQ(A, B)"); | 168 | cpu_write(3'h0, OP_ADD); |
| 175 | check_status(0,0,0,0, "EQ False Status"); | 169 | cpu_exec_and_read(3'h3, read_val); |
| 176 | 170 | check({TAG_NUM, 6'd0}, read_val, "Add 63+1 (Wrap)"); | |
| 177 | // 2.3 Mixed Type Equality (Number 10 vs Atom 10) -> Should be NIL (Bits differ in Tag) | 171 | check_status(0,0,1,1, "Add Overflow Status"); // Expect Carry=1, Zero=1 |
| 178 | cpu_write(3'h1, {TAG_NUM, 6'd10}); | 172 | |
| 179 | cpu_write(3'h2, {TAG_ATOM, 6'd10}); | 173 | // -------------------------------------------------------- |
| 180 | cpu_write(3'h0, OP_EQ); | 174 | // SCENARIO 2: Equality (EQ) Logic |
| 181 | cpu_exec_and_read(3'h3, read_val); | 175 | // -------------------------------------------------------- |
| 182 | check(VAL_NIL, read_val, "EQ(Num, Atom)"); | 176 | $display("\n--- Scenario 2: EQ Logic ---"); |
| 183 | 177 | ||
| 184 | // -------------------------------------------------------- | 178 | // 2.1 Atom Equality (True) |
| 185 | // SCENARIO 3: Linked List Construction (Chain Verification) | 179 | cpu_write(3'h1, VAL_A); |
| 186 | // -------------------------------------------------------- | 180 | cpu_write(3'h2, VAL_A); |
| 187 | $display("\n--- Scenario 3: Linked List (A B C) ---"); | 181 | cpu_write(3'h0, OP_EQ); |
| 188 | // Goal: Construct (A . (B . (C . NIL))) | 182 | cpu_exec_and_read(3'h3, read_val); |
| 189 | // Steps: | 183 | check(VAL_TRUE, read_val, "EQ(A, A)"); |
| 190 | // 1. Node3 = CONS(C, NIL) | 184 | check_status(0,0,0,1, "EQ True Status"); // Zero flag used for equality? Spec says "ZERO (From ADD or EQ op)" |
| 191 | // 2. Node2 = CONS(B, Node3) | 185 | |
| 192 | // 3. Node1 = CONS(A, Node2) | 186 | // 2.2 Atom Inequality (False) |
| 193 | 187 | cpu_write(3'h1, VAL_A); | |
| 194 | // Step 1: Node 3 | 188 | cpu_write(3'h2, VAL_B); |
| 195 | cpu_write(3'h1, VAL_C); | 189 | cpu_write(3'h0, OP_EQ); |
| 196 | cpu_write(3'h2, VAL_NIL); | 190 | cpu_exec_and_read(3'h3, read_val); |
| 197 | cpu_write(3'h0, OP_CONS); | 191 | check(VAL_NIL, read_val, "EQ(A, B)"); |
| 198 | cpu_exec_and_read(3'h3, ptr_node3); | 192 | check_status(0,0,0,0, "EQ False Status"); |
| 199 | check({TAG_CONS, 2'b00, 4'h0}, ptr_node3, "Alloc Node 3 (Ptr=0)"); | 193 | |
| 200 | 194 | // 2.3 Mixed Type Equality (Number 10 vs Atom 10) -> Should be NIL (Bits differ in Tag) | |
| 201 | // Step 2: Node 2 | 195 | cpu_write(3'h1, {TAG_NUM, 6'd10}); |
| 202 | cpu_write(3'h1, VAL_B); | 196 | cpu_write(3'h2, {TAG_ATOM, 6'd10}); |
| 203 | cpu_write(3'h2, ptr_node3); | 197 | cpu_write(3'h0, OP_EQ); |
| 204 | cpu_write(3'h0, OP_CONS); | 198 | cpu_exec_and_read(3'h3, read_val); |
| 205 | cpu_exec_and_read(3'h3, ptr_node2); | 199 | check(VAL_NIL, read_val, "EQ(Num, Atom)"); |
| 206 | check({TAG_CONS, 2'b00, 4'h2}, ptr_node2, "Alloc Node 2 (Ptr=2)"); | 200 | |
| 207 | 201 | // -------------------------------------------------------- | |
| 208 | // Step 3: Node 1 | 202 | // SCENARIO 3: Linked List Construction (Chain Verification) |
| 209 | cpu_write(3'h1, VAL_A); | 203 | // -------------------------------------------------------- |
| 210 | cpu_write(3'h2, ptr_node2); | 204 | $display("\n--- Scenario 3: Linked List (A B C) ---"); |
| 211 | cpu_write(3'h0, OP_CONS); | 205 | // Goal: Construct (A . (B . (C . NIL))) |
| 212 | cpu_exec_and_read(3'h3, ptr_node1); | 206 | // Steps: |
| 213 | check({TAG_CONS, 2'b00, 4'h4}, ptr_node1, "Alloc Node 1 (Ptr=4)"); | 207 | // 1. Node3 = CONS(C, NIL) |
| 214 | 208 | // 2. Node2 = CONS(B, Node3) | |
| 215 | // Step 4: Traverse! CAR(CDR(ptr_node1)) should be B | 209 | // 3. Node1 = CONS(A, Node2) |
| 216 | 210 | ||
| 217 | // CDR(Node1) -> Should get Node2 Ptr | 211 | // Step 1: Node 3 |
| 218 | cpu_write(3'h1, ptr_node1); | 212 | cpu_write(3'h1, VAL_C); |
| 219 | cpu_write(3'h0, OP_CDR); | 213 | cpu_write(3'h2, VAL_NIL); |
| 220 | cpu_exec_and_read(3'h3, read_val); | 214 | cpu_write(3'h0, OP_CONS); |
| 221 | check(ptr_node2, read_val, "Traverse: CDR(Node1)"); | 215 | cpu_exec_and_read(3'h3, ptr_node3); |
| 222 | 216 | check({TAG_CONS, 2'b00, 4'h0}, ptr_node3, "Alloc Node 3 (Ptr=0)"); | |
| 223 | // CAR(Result) -> Should get B | 217 | |
| 224 | cpu_write(3'h1, read_val); | 218 | // Step 2: Node 2 |
| 225 | cpu_write(3'h0, OP_CAR); | 219 | cpu_write(3'h1, VAL_B); |
| 226 | cpu_exec_and_read(3'h3, read_val); | 220 | cpu_write(3'h2, ptr_node3); |
| 227 | check(VAL_B, read_val, "Traverse: CAR(Node2)"); | 221 | cpu_write(3'h0, OP_CONS); |
| 228 | 222 | cpu_exec_and_read(3'h3, ptr_node2); | |
| 229 | // -------------------------------------------------------- | 223 | check({TAG_CONS, 2'b00, 4'h2}, ptr_node2, "Alloc Node 2 (Ptr=2)"); |
| 230 | // SCENARIO 4: Error Type Matrix | 224 | |
| 231 | // -------------------------------------------------------- | 225 | // Step 3: Node 1 |
| 232 | $display("\n--- Scenario 4: Type Safety ---"); | 226 | cpu_write(3'h1, VAL_A); |
| 233 | 227 | cpu_write(3'h2, ptr_node2); | |
| 234 | // 4.1 CAR on ATOM (Fail) | 228 | cpu_write(3'h0, OP_CONS); |
| 235 | cpu_write(3'h1, VAL_A); | 229 | cpu_exec_and_read(3'h3, ptr_node1); |
| 236 | cpu_write(3'h0, OP_CAR); | 230 | check({TAG_CONS, 2'b00, 4'h4}, ptr_node1, "Alloc Node 1 (Ptr=4)"); |
| 237 | check_status(0,1,0,0, "Err: CAR on Atom"); // Expect ErrType=1 | 231 | |
| 238 | 232 | // Step 4: Traverse! CAR(CDR(ptr_node1)) should be B | |
| 239 | // 4.2 CDR on NUMBER (Fail) | 233 | |
| 240 | cpu_write(3'h1, {TAG_NUM, 6'd5}); | 234 | // CDR(Node1) -> Should get Node2 Ptr |
| 241 | cpu_write(3'h0, OP_CDR); | 235 | cpu_write(3'h1, ptr_node1); |
| 242 | check_status(0,1,0,0, "Err: CDR on Number"); | 236 | cpu_write(3'h0, OP_CDR); |
| 243 | 237 | cpu_exec_and_read(3'h3, read_val); | |
| 244 | // 4.3 ADD on CONS (Fail) | 238 | check(ptr_node2, read_val, "Traverse: CDR(Node1)"); |
| 245 | cpu_write(3'h1, {TAG_NUM, 6'd5}); | 239 | |
| 246 | cpu_write(3'h2, ptr_node1); | 240 | // CAR(Result) -> Should get B |
| 247 | cpu_write(3'h0, OP_ADD); | 241 | cpu_write(3'h1, read_val); |
| 248 | check_status(0,1,0,0, "Err: ADD on CONS"); | 242 | cpu_write(3'h0, OP_CAR); |
| 249 | 243 | cpu_exec_and_read(3'h3, read_val); | |
| 250 | // -------------------------------------------------------- | 244 | check(VAL_B, read_val, "Traverse: CAR(Node2)"); |
| 251 | // SCENARIO 5: Heap Full Boundary | 245 | |
| 252 | // -------------------------------------------------------- | 246 | // -------------------------------------------------------- |
| 253 | $display("\n--- Scenario 5: Heap Full Boundary ---"); | 247 | // SCENARIO 4: Error Type Matrix |
| 254 | 248 | // -------------------------------------------------------- | |
| 255 | // Current Alloc Pointer is at 6 (We did 3 CONS ops: 0, 2, 4). | 249 | $display("\n--- Scenario 4: Type Safety ---"); |
| 256 | // Capacity is 16. Addresses 6, 8, 10, 12, 14 are free. | 250 | |
| 257 | // That is 5 more CONS operations allowed. | 251 | // 4.1 CAR on ATOM (Fail) |
| 258 | 252 | cpu_write(3'h1, VAL_A); | |
| 259 | // Fill 1 (Ptr 6) | 253 | cpu_write(3'h0, OP_CAR); |
| 260 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | 254 | check_status(0,1,0,0, "Err: CAR on Atom"); // Expect ErrType=1 |
| 261 | cpu_exec_and_read(3'h3, read_val); // Wait | 255 | |
| 262 | 256 | // 4.2 CDR on NUMBER (Fail) | |
| 263 | // Fill 2 (Ptr 8) | 257 | cpu_write(3'h1, {TAG_NUM, 6'd5}); |
| 264 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | 258 | cpu_write(3'h0, OP_CDR); |
| 265 | cpu_exec_and_read(3'h3, read_val); | 259 | check_status(0,1,0,0, "Err: CDR on Number"); |
| 266 | 260 | ||
| 267 | // Fill 3 (Ptr 10) | 261 | // 4.3 ADD on CONS (Fail) |
| 268 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | 262 | cpu_write(3'h1, {TAG_NUM, 6'd5}); |
| 269 | cpu_exec_and_read(3'h3, read_val); | 263 | cpu_write(3'h2, ptr_node1); |
| 270 | 264 | cpu_write(3'h0, OP_ADD); | |
| 271 | // Fill 4 (Ptr 12) | 265 | check_status(0,1,0,0, "Err: ADD on CONS"); |
| 272 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | 266 | |
| 273 | cpu_exec_and_read(3'h3, read_val); | 267 | // -------------------------------------------------------- |
| 274 | 268 | // SCENARIO 5: Heap Full Boundary | |
| 275 | // Fill 5 (Ptr 14) - THE LAST VALID ONE | 269 | // -------------------------------------------------------- |
| 276 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | 270 | $display("\n--- Scenario 5: Heap Full Boundary ---"); |
| 277 | cpu_exec_and_read(3'h3, read_val); | 271 | |
| 278 | check({TAG_CONS, 2'b00, 4'hE}, read_val, "Last Valid Alloc (Ptr=14)"); | 272 | // Current Alloc Pointer is at 6 (We did 3 CONS ops: 0, 2, 4). |
| 279 | check_status(0,0,0,0, "Status at Capacity"); | 273 | // Capacity is 16. Addresses 6, 8, 10, 12, 14 are free. |
| 280 | 274 | // That is 5 more CONS operations allowed. | |
| 281 | // ATTEMPT OVERFLOW | 275 | |
| 282 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | 276 | // Fill 1 (Ptr 6) |
| 283 | 277 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | |
| 284 | // Check Status | 278 | cpu_exec_and_read(3'h3, read_val); // Wait |
| 285 | cpu_exec_and_read(3'h4, status_val); | 279 | |
| 286 | // Expect ErrHeap=1 | 280 | // Fill 2 (Ptr 8) |
| 287 | if (status_val[1] !== 1'b1) begin | 281 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); |
| 288 | $display("FAIL: Heap Full Detection | Got Status: %b", status_val); | 282 | cpu_exec_and_read(3'h3, read_val); |
| 289 | errors = errors + 1; | 283 | |
| 290 | end else begin | 284 | // Fill 3 (Ptr 10) |
| 291 | $display("PASS: Heap Full Detection"); | 285 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); |
| 292 | end | 286 | cpu_exec_and_read(3'h3, read_val); |
| 293 | 287 | ||
| 294 | // -------------------------------------------------------- | 288 | // Fill 4 (Ptr 12) |
| 295 | // RESULT SUMMARY | 289 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); |
| 296 | // -------------------------------------------------------- | 290 | cpu_exec_and_read(3'h3, read_val); |
| 297 | $display("\n=================================="); | 291 | |
| 298 | if (errors == 0) | 292 | // Fill 5 (Ptr 14) - THE LAST VALID ONE |
| 299 | $display(" SUCCESS: All Tests Passed"); | 293 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); |
| 300 | else | 294 | cpu_exec_and_read(3'h3, read_val); |
| 301 | $display(" FAILURE: %0d Errors Found", errors); | 295 | check({TAG_CONS, 2'b00, 4'hE}, read_val, "Last Valid Alloc (Ptr=14)"); |
| 302 | $display("=================================="); | 296 | check_status(0,0,0,0, "Status at Capacity"); |
| 303 | $finish; | 297 | |
| 304 | end | 298 | // ATTEMPT OVERFLOW |
| 299 | cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS); | ||
| 300 | |||
| 301 | // Check Status | ||
| 302 | cpu_exec_and_read(3'h4, status_val); | ||
| 303 | // Expect ErrHeap=1 | ||
| 304 | if (status_val[1] !== 1'b1) begin | ||
| 305 | $display("FAIL: Heap Full Detection | Got Status: %b", status_val); | ||
| 306 | errors = errors + 1; | ||
| 307 | end else begin | ||
| 308 | $display("PASS: Heap Full Detection"); | ||
| 309 | end | ||
| 310 | |||
| 311 | // -------------------------------------------------------- | ||
| 312 | // RESULT SUMMARY | ||
| 313 | // -------------------------------------------------------- | ||
| 314 | $display("\n=================================="); | ||
| 315 | if (errors == 0) | ||
| 316 | $display(" SUCCESS: All Tests Passed"); | ||
| 317 | else | ||
| 318 | $display(" FAILURE: %0d Errors Found", errors); | ||
| 319 | $display("=================================="); | ||
| 320 | $finish; | ||
| 321 | end | ||
| 305 | 322 | ||
| 306 | endmodule | 323 | endmodule |
diff --git a/vlsi/tb_chip_core.v b/vlsi/tb_chip_core.v index 66fa212..b4a8925 100644 --- a/vlsi/tb_chip_core.v +++ b/vlsi/tb_chip_core.v | |||
| @@ -115,13 +115,13 @@ module tb_chip_core; | |||
| 115 | // ======================================================================== | 115 | // ======================================================================== |
| 116 | 116 | ||
| 117 | initial begin | 117 | initial begin |
| 118 | $dumpfile("chip_core_robust.vcd"); | 118 | $dumpfile("chip_core.vcd"); |
| 119 | $dumpvars(0, tb_chip_core); | 119 | $dumpvars(0, tb_chip_core); |
| 120 | 120 | ||
| 121 | // --- Initialize --- | 121 | // --- Initialize --- |
| 122 | rst = 1; cs = 0; rw = 0; addr = 0; data_in = 0; | 122 | rst = 1; cs = 0; rw = 0; addr = 0; data_in = 0; |
| 123 | #20 rst = 0; #20; | 123 | #20 rst = 0; #20; |
| 124 | $display("\n=== STARTING ROBUST VERIFICATION ===\n"); | 124 | $display("\n=== STARTING VERIFICATION ===\n"); |
| 125 | 125 | ||
| 126 | // -------------------------------------------------------- | 126 | // -------------------------------------------------------- |
| 127 | // SCENARIO 1: ALU Boundary & Overflow | 127 | // SCENARIO 1: ALU Boundary & Overflow |
