diff options
| author | vin <git@vineetk.net> | 2025-12-10 16:35:42 -0500 |
|---|---|---|
| committer | vin <git@vineetk.net> | 2025-12-10 16:39:36 -0500 |
| commit | 06b6b116934cc8341676f52f5f6040a038020c32 (patch) | |
| tree | f2db3531293b6de1a67a1e26a2aa1663f4d55082 /rtl | |
| parent | 36f4ec74c5d1247362bed901dbaf0a36970b4cd0 (diff) | |
this is quite a major shift. moves the project from a manual,
full-custom layout workflow to a more modern automated flow and a more
modern process (130nm vs 350nm). also improved the testbench.
migration also resolves previous manual routing-caused errors (like
the CONS logic bug and difficulty in implementing TG
logic).
this nearly halved the transistor count from 15k to 8k and reduced
area size by 300x.
Diffstat (limited to 'rtl')
| -rw-r--r-- | rtl/lisp_coproc.sv | 453 | ||||
| -rw-r--r-- | rtl/tb_lisp_coproc.sv | 611 |
2 files changed, 556 insertions, 508 deletions
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 |
