diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | rtl/lisp_coproc.sv | 323 | ||||
| -rw-r--r-- | rtl/tb_lisp_coproc.sv | 231 |
3 files changed, 240 insertions, 317 deletions
| @@ -1,3 +1,4 @@ | |||
| 1 | rtl/lisp_coproc_sim | 1 | rtl/lisp_coproc_sim |
| 2 | *.vcd | ||
| 2 | *~ | 3 | *~ |
| 3 | #* \ No newline at end of file | 4 | #* |
diff --git a/rtl/lisp_coproc.sv b/rtl/lisp_coproc.sv index bd3057a..b855b7b 100644 --- a/rtl/lisp_coproc.sv +++ b/rtl/lisp_coproc.sv | |||
| @@ -8,248 +8,195 @@ module lisp_coproc ( | |||
| 8 | output reg [7:0] data_out | 8 | output reg [7:0] data_out |
| 9 | ); | 9 | ); |
| 10 | 10 | ||
| 11 | // FSM States (One-Hot Encoding) | 11 | // ======================================================================== |
| 12 | parameter [4:0] RESET = 5'b00001; | 12 | // 1. DATAPATH Signals & Storage |
| 13 | parameter [4:0] IDLE = 5'b00010; | 13 | // ======================================================================== |
| 14 | parameter [4:0] DECODE = 5'b00100; | 14 | |
| 15 | parameter [4:0] EXECUTE = 5'b01000; | 15 | // Registers |
| 16 | parameter [4:0] WRITEBACK= 5'b10000; | 16 | reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg; |
| 17 | 17 | reg [7:0] heap [0:15]; | |
| 18 | // Internal Registers | 18 | |
| 19 | reg [4:0] state, next_state; | 19 | // Bump Allocator: Uniform 4-bit register (0-15) |
| 20 | reg [7:0] opcode_reg, arg1_reg, arg2_reg, result_reg, status_reg; | 20 | reg [3:0] bump_alloc; |
| 21 | reg [7:0] heap [0:15]; // 16 entries x 8 bits | ||
| 22 | 21 | ||
| 23 | // FIX: Bump allocator must be 5 bits to hold the value '16' (Full) | 22 | // Heap Status: Sticky bit to track if we have wrapped around (Full) |
| 24 | // without wrapping around to 0. | 23 | reg heap_filled; |
| 25 | reg [4:0] bump_alloc; | ||
| 26 | 24 | ||
| 27 | // Status Register Bits | 25 | // Internal Flags (Transient for current OP) |
| 28 | wire busy = (state != IDLE); | 26 | reg flag_err_heap, flag_err_type, flag_carry, flag_zero; |
| 29 | reg err_heap_full, err_type, carry, zero; | ||
| 30 | 27 | ||
| 31 | // ALU Signals | 28 | // ALU Signals |
| 32 | reg [5:0] alu_a, alu_b; | 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_cons_b = (arg2_reg[7:6] == 2'b11); | ||
| 37 | wire is_num_a = (arg1_reg[7:6] == 2'b10); | ||
| 38 | wire is_num_b = (arg2_reg[7:6] == 2'b10); | ||
| 39 | |||
| 40 | // Allocation Logic (Datapath Adder) | ||
| 41 | // We use a 5-bit wire to capture the carry out. | ||
| 42 | // If bump_alloc is 14 (1110) + 2 = 16 (10000). | ||
| 43 | // alloc_sum[4] (Carry) is 1. alloc_sum[3:0] is 0000. | ||
| 44 | wire [4:0] alloc_sum = {1'b0, bump_alloc} + 5'd2; | ||
| 45 | wire alloc_carry = alloc_sum[4]; | ||
| 46 | |||
| 47 | // ======================================================================== | ||
| 48 | // 2. FSM CONTROLLER | ||
| 49 | // ======================================================================== | ||
| 33 | 50 | ||
| 34 | // Temporary registers for operations | 51 | parameter [4:0] RESET = 5'b00001; |
| 35 | reg [7:0] temp_result; | 52 | parameter [4:0] IDLE = 5'b00010; |
| 36 | reg [3:0] temp_ptr; | 53 | parameter [4:0] DECODE = 5'b00100; |
| 54 | parameter [4:0] EXECUTE = 5'b01000; | ||
| 55 | parameter [4:0] WRITEBACK = 5'b10000; | ||
| 56 | |||
| 57 | reg [4:0] state, next_state; | ||
| 37 | 58 | ||
| 38 | // FSM State Transition | ||
| 39 | always @(posedge clk or posedge rst) begin | 59 | always @(posedge clk or posedge rst) begin |
| 40 | if (rst) begin | 60 | if (rst) state <= RESET; |
| 41 | state <= RESET; | 61 | else state <= next_state; |
| 42 | end else begin | ||
| 43 | state <= next_state; | ||
| 44 | end | ||
| 45 | end | 62 | end |
| 46 | 63 | ||
| 47 | // FSM Combinational Logic | ||
| 48 | always @(*) begin | 64 | always @(*) begin |
| 49 | next_state = state; | 65 | next_state = state; |
| 50 | |||
| 51 | case (state) | 66 | case (state) |
| 52 | RESET: begin | 67 | RESET: next_state = IDLE; |
| 53 | next_state = IDLE; | 68 | IDLE: if (cs && !rw && addr == 3'h0) next_state = DECODE; |
| 54 | end | 69 | DECODE: next_state = EXECUTE; |
| 55 | 70 | EXECUTE: next_state = WRITEBACK; | |
| 56 | IDLE: begin | 71 | WRITEBACK: next_state = IDLE; |
| 57 | if (cs && !rw && addr == 3'h0) begin // Writing to OPCODE triggers operation | 72 | default: next_state = IDLE; |
| 58 | next_state = DECODE; | ||
| 59 | end | ||
| 60 | end | ||
| 61 | |||
| 62 | DECODE: begin | ||
| 63 | next_state = EXECUTE; | ||
| 64 | end | ||
| 65 | |||
| 66 | EXECUTE: begin | ||
| 67 | next_state = WRITEBACK; | ||
| 68 | end | ||
| 69 | |||
| 70 | WRITEBACK: begin | ||
| 71 | next_state = IDLE; | ||
| 72 | end | ||
| 73 | |||
| 74 | default: begin | ||
| 75 | next_state = IDLE; | ||
| 76 | end | ||
| 77 | endcase | 73 | endcase |
| 78 | end | 74 | end |
| 79 | 75 | ||
| 80 | // Register File and Memory Interface | 76 | // ======================================================================== |
| 77 | // 3. SEQUENTIAL LOGIC | ||
| 78 | // ======================================================================== | ||
| 79 | |||
| 80 | integer i; | ||
| 81 | always @(posedge clk or posedge rst) begin | 81 | always @(posedge clk or posedge rst) begin |
| 82 | if (rst) begin | 82 | if (rst) begin |
| 83 | opcode_reg <= 8'h00; | 83 | opcode_reg <= 8'h00; |
| 84 | arg1_reg <= 8'h00; | 84 | arg1_reg <= 8'h00; |
| 85 | arg2_reg <= 8'h00; | 85 | arg2_reg <= 8'h00; |
| 86 | result_reg <= 8'h00; | 86 | result_reg <= 8'h00; |
| 87 | status_reg <= 8'h00; | 87 | bump_alloc <= 4'h0; |
| 88 | bump_alloc <= 5'h00; // Reset 5-bit register | 88 | heap_filled <= 1'b0; |
| 89 | err_heap_full <= 1'b0; | 89 | |
| 90 | err_type <= 1'b0; | 90 | flag_err_heap <= 1'b0; |
| 91 | carry <= 1'b0; | 91 | flag_err_type <= 1'b0; |
| 92 | zero <= 1'b0; | 92 | flag_carry <= 1'b0; |
| 93 | flag_zero <= 1'b0; | ||
| 94 | |||
| 95 | for (i=0; i<16; i=i+1) heap[i] <= 8'h00; | ||
| 96 | |||
| 93 | end else begin | 97 | end else begin |
| 94 | // Memory-mapped register writes | 98 | |
| 99 | // --- MMIO Writes --- | ||
| 95 | if (cs && !rw) begin | 100 | if (cs && !rw) begin |
| 96 | case (addr) | 101 | case (addr) |
| 97 | 3'h0: opcode_reg <= data_in; | 102 | 3'h0: opcode_reg <= data_in; |
| 98 | 3'h1: arg1_reg <= data_in; | 103 | 3'h1: arg1_reg <= data_in; |
| 99 | 3'h2: arg2_reg <= data_in; | 104 | 3'h2: arg2_reg <= data_in; |
| 100 | 3'h3: result_reg <= data_in; // Direct write to result | 105 | 3'h3: result_reg <= data_in; |
| 101 | 3'h4: status_reg <= data_in; // Direct write to status | ||
| 102 | endcase | 106 | endcase |
| 103 | end | 107 | end |
| 104 | 108 | ||
| 105 | // FSM State-specific operations | 109 | // --- State Actions --- |
| 106 | case (state) | 110 | case (state) |
| 107 | RESET: begin | 111 | RESET: begin |
| 108 | // Clear heap on reset | 112 | bump_alloc <= 4'h0; |
| 109 | integer i; | 113 | heap_filled <= 1'b0; |
| 110 | for (i = 0; i < 16; i = i + 1) begin | ||
| 111 | heap[i] <= 8'h00; | ||
| 112 | end | ||
| 113 | bump_alloc <= 5'h00; | ||
| 114 | opcode_reg <= 8'h00; | ||
| 115 | arg1_reg <= 8'h00; | ||
| 116 | arg2_reg <= 8'h00; | ||
| 117 | result_reg <= 8'h00; | ||
| 118 | status_reg <= 8'h00; | ||
| 119 | end | 114 | end |
| 120 | 115 | ||
| 121 | IDLE: begin | 116 | IDLE: begin |
| 122 | // Only clear flags when a NEW operation starts. | ||
| 123 | if (cs && !rw && addr == 3'h0) begin | 117 | if (cs && !rw && addr == 3'h0) begin |
| 124 | err_heap_full <= 1'b0; | 118 | flag_err_heap <= 1'b0; |
| 125 | err_type <= 1'b0; | 119 | flag_err_type <= 1'b0; |
| 126 | carry <= 1'b0; | 120 | flag_carry <= 1'b0; |
| 127 | zero <= 1'b0; | 121 | flag_zero <= 1'b0; |
| 128 | end | 122 | end |
| 129 | end | 123 | end |
| 130 | 124 | ||
| 131 | EXECUTE: begin | 125 | EXECUTE: begin |
| 132 | // Assign ALU inputs for ADD operation using BLOCKING assignment | ||
| 133 | alu_a = arg1_reg[5:0]; | ||
| 134 | alu_b = arg2_reg[5:0]; | ||
| 135 | |||
| 136 | case (opcode_reg) | 126 | case (opcode_reg) |
| 137 | // CONS operation | 127 | 8'h01: begin // CONS |
| 138 | 8'h01: begin | 128 | if (heap_filled) begin |
| 139 | // 5-bit arithmetic: 16 + 2 = 18. 18 > 16 is TRUE. | 129 | // If sticky flag is set, we are full. Error. |
| 140 | if (bump_alloc + 2 > 16) begin | 130 | flag_err_heap <= 1'b1; |
| 141 | err_heap_full <= 1'b1; | ||
| 142 | end else begin | 131 | end else begin |
| 143 | // Store ARG1 and ARG2 in heap | 132 | // Perform allocation |
| 144 | heap[bump_alloc[3:0]] <= arg1_reg; | 133 | heap[bump_alloc] <= arg1_reg; |
| 145 | heap[bump_alloc[3:0] + 1] <= arg2_reg; | 134 | heap[bump_alloc + 1] <= arg2_reg; |
| 146 | // Return CONS tag with pointer as value | 135 | |
| 147 | temp_ptr <= bump_alloc[3:0]; | 136 | // Update pointer (wraps automatically due to 4-bit) |
| 148 | bump_alloc <= bump_alloc + 2; | 137 | bump_alloc <= alloc_sum[3:0]; |
| 138 | |||
| 139 | // If we generated a carry (14->16), mark heap as filled | ||
| 140 | if (alloc_carry) heap_filled <= 1'b1; | ||
| 149 | end | 141 | end |
| 150 | end | 142 | end |
| 151 | 143 | 8'h02: begin // CAR | |
| 152 | // CAR operation | 144 | if (!is_cons_a) flag_err_type <= 1'b1; |
| 153 | 8'h02: begin | ||
| 154 | if (arg1_reg[7:6] != 2'b11) begin // Not a CONS | ||
| 155 | err_type <= 1'b1; | ||
| 156 | end else begin | ||
| 157 | temp_result <= heap[arg1_reg[3:0]]; | ||
| 158 | end | ||
| 159 | end | 145 | end |
| 160 | 146 | 8'h03: begin // CDR | |
| 161 | // CDR operation | 147 | if (!is_cons_a) flag_err_type <= 1'b1; |
| 162 | 8'h03: begin | ||
| 163 | if (arg1_reg[7:6] != 2'b11) begin // Not a CONS | ||
| 164 | err_type <= 1'b1; | ||
| 165 | end else begin | ||
| 166 | temp_result <= heap[arg1_reg[3:0] + 1]; | ||
| 167 | end | ||
| 168 | end | 148 | end |
| 169 | 149 | 8'h05: begin // EQ | |
| 170 | // ATOM operation | 150 | if (alu_eq) flag_zero <= 1'b1; |
| 171 | 8'h04: begin | ||
| 172 | if (arg1_reg[7:6] == 2'b11) begin // Is a CONS | ||
| 173 | temp_result <= 8'h00; // NIL | ||
| 174 | end else begin | ||
| 175 | temp_result <= 8'h41; // 'T' (01_000001) | ||
| 176 | end | ||
| 177 | end | 151 | end |
| 178 | 152 | 8'h06: begin // ADD | |
| 179 | // EQ operation | 153 | if (!is_num_a || !is_num_b) begin |
| 180 | 8'h05: begin | 154 | flag_err_type <= 1'b1; |
| 181 | if (arg1_reg == arg2_reg) begin | ||
| 182 | temp_result <= 8'h41; // 'T' (01_000001) | ||
| 183 | zero <= 1'b1; | ||
| 184 | end else begin | 155 | end else begin |
| 185 | temp_result <= 8'h00; // NIL | 156 | if (alu_sum[6]) flag_carry <= 1'b1; |
| 186 | end | 157 | if (alu_sum[5:0] == 6'd0) flag_zero <= 1'b1; |
| 187 | end | ||
| 188 | |||
| 189 | // ADD operation | ||
| 190 | 8'h06: begin | ||
| 191 | if (arg1_reg[7:6] != 2'b10 || arg2_reg[7:6] != 2'b10) begin // Not both NUMBERs | ||
| 192 | err_type <= 1'b1; | ||
| 193 | end else begin | ||
| 194 | if ((alu_a + alu_b) > 6'd63) begin | ||
| 195 | carry <= 1'b1; | ||
| 196 | end | ||
| 197 | if ((alu_a + alu_b) == 6'd0) begin | ||
| 198 | zero <= 1'b1; | ||
| 199 | end | ||
| 200 | temp_result <= {2'b10, alu_a + alu_b}; // NUMBER tag with sum | ||
| 201 | end | 158 | end |
| 202 | end | 159 | end |
| 203 | endcase | 160 | endcase |
| 204 | end | 161 | end |
| 205 | 162 | ||
| 206 | WRITEBACK: begin | 163 | WRITEBACK: begin |
| 207 | // Write the result based on the operation | ||
| 208 | case (opcode_reg) | 164 | case (opcode_reg) |
| 209 | 8'h01: begin // CONS | 165 | 8'h01: begin // CONS |
| 210 | if (!err_heap_full) begin | 166 | if (!flag_err_heap) |
| 211 | // Tag [7:6] must be set correctly. | 167 | // Math trick: If bump_alloc wrapped to 0, |
| 212 | result_reg <= {2'b11, 2'b00, temp_ptr}; | 168 | // 0 - 2 = 14 (1110 in 2's comp), which is the correct pointer. |
| 213 | end | 169 | result_reg <= {2'b11, 2'b00, bump_alloc - 4'd2}; |
| 214 | end | 170 | end |
| 215 | 171 | 8'h02: begin // CAR | |
| 216 | 8'h02: begin // CAR | 172 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0]]; |
| 217 | if (!err_type) begin | ||
| 218 | result_reg <= temp_result; | ||
| 219 | end | ||
| 220 | end | 173 | end |
| 221 | 174 | 8'h03: begin // CDR | |
| 222 | 8'h03: begin // CDR | 175 | if (!flag_err_type) result_reg <= heap[arg1_reg[3:0] + 1]; |
| 223 | if (!err_type) begin | ||
| 224 | result_reg <= temp_result; | ||
| 225 | end | ||
| 226 | end | 176 | end |
| 227 | 177 | 8'h04: begin // ATOM | |
| 228 | 8'h04: begin // ATOM | 178 | result_reg <= is_cons_a ? 8'h00 : 8'h41; |
| 229 | result_reg <= temp_result; | ||
| 230 | end | 179 | end |
| 231 | 180 | 8'h05: begin // EQ | |
| 232 | 8'h05: begin // EQ | 181 | result_reg <= alu_eq ? 8'h41 : 8'h00; |
| 233 | result_reg <= temp_result; | ||
| 234 | end | 182 | end |
| 235 | 183 | 8'h06: begin // ADD | |
| 236 | 8'h06: begin // ADD | 184 | if (!flag_err_type) result_reg <= {2'b10, alu_sum[5:0]}; |
| 237 | if (!err_type) begin | ||
| 238 | result_reg <= temp_result; | ||
| 239 | end | ||
| 240 | end | 185 | end |
| 241 | endcase | 186 | endcase |
| 242 | end | 187 | end |
| 243 | endcase | 188 | endcase |
| 244 | end | 189 | end |
| 245 | end | 190 | end |
| 191 | |||
| 192 | // ======================================================================== | ||
| 193 | // 4. OUTPUT LOGIC | ||
| 194 | // ======================================================================== | ||
| 246 | 195 | ||
| 247 | // Update status register | 196 | wire busy_bit = (state != IDLE); |
| 248 | always @(*) begin | 197 | // Note: bit 1 is the transient error flag, not the internal sticky state |
| 249 | status_reg = {3'b000, zero, carry, err_type, err_heap_full, busy}; | 198 | wire [7:0] current_status = {3'b000, flag_zero, flag_carry, flag_err_type, flag_err_heap, busy_bit}; |
| 250 | end | 199 | |
| 251 | |||
| 252 | // Output Logic | ||
| 253 | always @(*) begin | 200 | always @(*) begin |
| 254 | if (cs && rw) begin | 201 | if (cs && rw) begin |
| 255 | case (addr) | 202 | case (addr) |
| @@ -257,11 +204,11 @@ module lisp_coproc ( | |||
| 257 | 3'h1: data_out = arg1_reg; | 204 | 3'h1: data_out = arg1_reg; |
| 258 | 3'h2: data_out = arg2_reg; | 205 | 3'h2: data_out = arg2_reg; |
| 259 | 3'h3: data_out = result_reg; | 206 | 3'h3: data_out = result_reg; |
| 260 | 3'h4: data_out = status_reg; | 207 | 3'h4: data_out = current_status; |
| 261 | default: data_out = 8'h00; | 208 | default: data_out = 8'h00; |
| 262 | endcase | 209 | endcase |
| 263 | end else begin | 210 | end else begin |
| 264 | data_out = 8'hZZ; // High impedance when not reading | 211 | data_out = 8'hZZ; |
| 265 | end | 212 | end |
| 266 | end | 213 | end |
| 267 | 214 | ||
diff --git a/rtl/tb_lisp_coproc.sv b/rtl/tb_lisp_coproc.sv index 6c39aa4..aa721fc 100644 --- a/rtl/tb_lisp_coproc.sv +++ b/rtl/tb_lisp_coproc.sv | |||
| @@ -2,170 +2,145 @@ | |||
| 2 | 2 | ||
| 3 | module tb_lisp_coproc; | 3 | module tb_lisp_coproc; |
| 4 | 4 | ||
| 5 | // Testbench signals | 5 | // Signals |
| 6 | reg clk; | 6 | reg clk, rst, cs, rw; |
| 7 | reg rst; | ||
| 8 | reg cs; | ||
| 9 | reg rw; // 0=Write, 1=Read | ||
| 10 | reg [2:0] addr; | 7 | reg [2:0] addr; |
| 11 | reg [7:0] data_in; | 8 | reg [7:0] data_in; |
| 12 | wire [7:0] data_out; | 9 | wire [7:0] data_out; |
| 13 | 10 | ||
| 14 | // Test variables | 11 | // Verification Vars |
| 15 | reg [7:0] read_data; | 12 | reg [7:0] read_data; |
| 16 | integer i; | 13 | integer i; |
| 17 | 14 | ||
| 18 | // Instantiate the DUT | 15 | // DUT Instance |
| 19 | lisp_coproc dut ( | 16 | lisp_coproc dut ( |
| 20 | .clk(clk), | 17 | .clk(clk), .rst(rst), .cs(cs), .rw(rw), |
| 21 | .rst(rst), | 18 | .addr(addr), .data_in(data_in), .data_out(data_out) |
| 22 | .cs(cs), | ||
| 23 | .rw(rw), | ||
| 24 | .addr(addr), | ||
| 25 | .data_in(data_in), | ||
| 26 | .data_out(data_out) | ||
| 27 | ); | 19 | ); |
| 28 | 20 | ||
| 29 | // Clock generation | 21 | // 100MHz Clock |
| 30 | initial begin | 22 | initial begin |
| 31 | clk = 0; | 23 | clk = 0; |
| 32 | forever #5 clk = ~clk; | 24 | forever #5 clk = ~clk; |
| 33 | end | 25 | end |
| 34 | 26 | ||
| 35 | // CPU Write Task | 27 | // --- CPU Bus Tasks --- |
| 36 | task cpu_write; | 28 | |
| 37 | input [2:0] addr_in; | 29 | task cpu_write(input [2:0] w_addr, input [7:0] w_data); |
| 38 | input [7:0] data_in_in; | ||
| 39 | begin | 30 | begin |
| 40 | @(posedge clk); | 31 | @(posedge clk); |
| 41 | cs = 1'b1; | 32 | cs = 1; rw = 0; addr = w_addr; data_in = w_data; |
| 42 | rw = 1'b0; // Write | ||
| 43 | addr = addr_in; | ||
| 44 | data_in = data_in_in; | ||
| 45 | @(posedge clk); | 33 | @(posedge clk); |
| 46 | cs = 1'b0; | 34 | cs = 0; data_in = 8'h00; // Clear bus |
| 47 | end | 35 | end |
| 48 | endtask | 36 | endtask |
| 49 | 37 | ||
| 50 | // CPU Read Task | 38 | task cpu_read(input [2:0] r_addr, output [7:0] r_data); |
| 51 | task cpu_read; | 39 | reg [7:0] stat; |
| 52 | input [2:0] addr_in; | ||
| 53 | output [7:0] data_out_out; | ||
| 54 | reg [7:0] status; | ||
| 55 | begin | 40 | begin |
| 56 | // Poll until not busy | 41 | // 1. Poll Status for BUSY=0 |
| 57 | do begin | 42 | do begin |
| 58 | @(posedge clk); | 43 | @(posedge clk); |
| 59 | cs = 1'b1; | 44 | cs = 1; rw = 1; addr = 3'h4; // Status Reg |
| 60 | rw = 1'b1; // Read | ||
| 61 | addr = 3'h4; // Status register | ||
| 62 | @(posedge clk); | 45 | @(posedge clk); |
| 63 | status = data_out; | 46 | stat = data_out; |
| 64 | cs = 1'b0; | 47 | cs = 0; |
| 65 | end while (status[0]); // Check BUSY bit | 48 | end while (stat[0] == 1'b1); |
| 66 | 49 | ||
| 67 | // Read the requested address | 50 | // 2. Perform Actual Read |
| 68 | @(posedge clk); | 51 | @(posedge clk); |
| 69 | cs = 1'b1; | 52 | cs = 1; rw = 1; addr = r_addr; |
| 70 | rw = 1'b1; // Read | ||
| 71 | addr = addr_in; | ||
| 72 | @(posedge clk); | 53 | @(posedge clk); |
| 73 | data_out_out = data_out; | 54 | r_data = data_out; |
| 74 | cs = 1'b0; | 55 | cs = 0; |
| 75 | end | 56 | end |
| 76 | endtask | 57 | endtask |
| 77 | 58 | ||
| 78 | // Test Sequence | 59 | // --- Main Test Sequence --- |
| 79 | initial begin | 60 | initial begin |
| 80 | // Initialize signals | 61 | $dumpfile("lisp_coproc.vcd"); |
| 81 | rst = 1'b1; | 62 | $dumpvars(0, tb_lisp_coproc); |
| 82 | cs = 1'b0; | 63 | |
| 83 | rw = 1'b0; | 64 | // Init |
| 84 | addr = 3'h0; | 65 | rst = 1; cs = 0; rw = 0; addr = 0; data_in = 0; |
| 85 | data_in = 8'h00; | 66 | #20 rst = 0; #20; |
| 86 | |||
| 87 | // Apply reset | ||
| 88 | #20; | ||
| 89 | rst = 1'b0; | ||
| 90 | #20; | ||
| 91 | |||
| 92 | // Test 1: Reset Test | ||
| 93 | $display("Test 1: Reset Test"); | ||
| 94 | cpu_read(3'h4, read_data); // Read STATUS | ||
| 95 | $display("STATUS after reset: 0x%02h (expected: 0x00)", read_data); | ||
| 96 | if (read_data != 8'h00) $display("ERROR: Reset test failed"); | ||
| 97 | |||
| 98 | // Test 2: ADD Test | ||
| 99 | $display("\nTest 2: ADD Test"); | ||
| 100 | cpu_write(3'h1, 8'h85); // ARG1 = NUMBER(5) | ||
| 101 | cpu_write(3'h2, 8'h86); // ARG2 = NUMBER(6) | ||
| 102 | cpu_write(3'h0, 8'h06); // OPCODE = ADD | ||
| 103 | |||
| 104 | cpu_read(3'h3, read_data); // Read RESULT | ||
| 105 | $display("ADD Result: 0x%02h (expected: 0x8B, NUMBER(11))", read_data); | ||
| 106 | if (read_data != 8'h8B) $display("ERROR: ADD test failed"); | ||
| 107 | |||
| 108 | cpu_read(3'h4, read_data); // Read STATUS | ||
| 109 | $display("STATUS after ADD: 0x%02h (expected: 0x00, ZERO=0)", read_data); | ||
| 110 | if (read_data != 8'h00) $display("ERROR: ADD status test failed"); | ||
| 111 | |||
| 112 | // Test 3: CONS Test | ||
| 113 | $display("\nTest 3: CONS Test"); | ||
| 114 | cpu_write(3'h1, 8'h41); // ARG1 = ATOM('T') | ||
| 115 | cpu_write(3'h2, 8'h42); // ARG2 = ATOM('B') | ||
| 116 | cpu_write(3'h0, 8'h01); // OPCODE = CONS | ||
| 117 | |||
| 118 | cpu_read(3'h3, read_data); // Read RESULT | ||
| 119 | $display("CONS Result: 0x%02h (expected: 0xC0, CONS(0))", read_data); | ||
| 120 | if (read_data != 8'hC0) $display("ERROR: CONS test failed"); | ||
| 121 | |||
| 122 | // Test CAR and CDR | ||
| 123 | cpu_write(3'h1, read_data); // Use the CONS result as ARG1 | ||
| 124 | cpu_write(3'h0, 8'h02); // OPCODE = CAR | ||
| 125 | |||
| 126 | cpu_read(3'h3, read_data); // Read RESULT | ||
| 127 | $display("CAR Result: 0x%02h (expected: 0x41, ATOM('T'))", read_data); | ||
| 128 | if (read_data != 8'h41) $display("ERROR: CAR test failed"); | ||
| 129 | |||
| 130 | cpu_write(3'h1, 8'hC0); // ARG1 = CONS(0) | ||
| 131 | cpu_write(3'h0, 8'h03); // OPCODE = CDR | ||
| 132 | |||
| 133 | cpu_read(3'h3, read_data); // Read RESULT | ||
| 134 | $display("CDR Result: 0x%02h (expected: 0x42, ATOM('B'))", read_data); | ||
| 135 | if (read_data != 8'h42) $display("ERROR: CDR test failed"); | ||
| 136 | |||
| 137 | // Test 4: Error Test (CAR on a Number) | ||
| 138 | $display("\nTest 4: Error Test (CAR on a Number)"); | ||
| 139 | cpu_write(3'h1, 8'h85); // ARG1 = NUMBER(5) | ||
| 140 | cpu_write(3'h0, 8'h02); // OPCODE = CAR | ||
| 141 | |||
| 142 | cpu_read(3'h4, read_data); // Read STATUS | ||
| 143 | $display("STATUS after CAR on Number: 0x%02h (expected: 0x04, ERR_TYPE=1)", read_data); | ||
| 144 | if (read_data != 8'h04) $display("ERROR: Error test failed"); | ||
| 145 | 67 | ||
| 146 | // Test 5: Heap Full Test | 68 | $display("--- Starting Lisp Coprocessor Verification ---"); |
| 147 | $display("\nTest 5: Heap Full Test"); | 69 | |
| 148 | // Fill the heap with CONS operations | 70 | // 1. Reset Test |
| 149 | // Since bump_alloc is now 2, we have used 2 cells. | 71 | cpu_read(3'h4, read_data); |
| 150 | // 16 cells total. 14 left. 7 more CONS possible. | 72 | assert_equals(8'h00, read_data, "Reset Status"); |
| 151 | for (i = 0; i < 7; i = i + 1) begin | 73 | |
| 152 | cpu_write(3'h1, 8'h41); | 74 | // 2. ADD Test (5 + 6 = 11) |
| 153 | cpu_write(3'h2, 8'h42); | 75 | $display("\n[Test] ADD Operation"); |
| 154 | cpu_write(3'h0, 8'h01); | 76 | cpu_write(3'h1, 8'h85); // Number(5) |
| 155 | cpu_read(3'h3, read_data); | 77 | cpu_write(3'h2, 8'h86); // Number(6) |
| 78 | cpu_write(3'h0, 8'h06); // OPCODE: ADD | ||
| 79 | cpu_read(3'h3, read_data); | ||
| 80 | assert_equals(8'h8B, read_data, "ADD Result (11)"); | ||
| 81 | cpu_read(3'h4, read_data); | ||
| 82 | assert_equals(8'h00, read_data, "ADD Status (Clean)"); | ||
| 83 | |||
| 84 | // 3. CONS Test | ||
| 85 | $display("\n[Test] CONS Operation"); | ||
| 86 | cpu_write(3'h1, 8'h41); // Atom('T') | ||
| 87 | cpu_write(3'h2, 8'h42); // Atom('B') | ||
| 88 | cpu_write(3'h0, 8'h01); // OPCODE: CONS | ||
| 89 | cpu_read(3'h3, read_data); | ||
| 90 | // Expect CONS Tag (11) | Address (0000) -> 0xC0 | ||
| 91 | assert_equals(8'hC0, read_data, "CONS Pointer"); | ||
| 92 | |||
| 93 | // Verify Heap Persistence via CAR/CDR | ||
| 94 | cpu_write(3'h1, 8'hC0); // Pointer to just allocated cell | ||
| 95 | cpu_write(3'h0, 8'h02); // OPCODE: CAR | ||
| 96 | cpu_read(3'h3, read_data); | ||
| 97 | assert_equals(8'h41, read_data, "CAR Check"); | ||
| 98 | |||
| 99 | cpu_write(3'h1, 8'hC0); | ||
| 100 | cpu_write(3'h0, 8'h03); // OPCODE: CDR | ||
| 101 | cpu_read(3'h3, read_data); | ||
| 102 | assert_equals(8'h42, read_data, "CDR Check"); | ||
| 103 | |||
| 104 | // 4. Error Test (Type Error) | ||
| 105 | $display("\n[Test] Error Handling (Type)"); | ||
| 106 | cpu_write(3'h1, 8'h85); // Number | ||
| 107 | cpu_write(3'h0, 8'h02); // OPCODE: CAR (Invalid on Number) | ||
| 108 | cpu_read(3'h4, read_data); | ||
| 109 | // Bit 2 (ERR_TYPE) should be set -> 0x04 | ||
| 110 | assert_equals(8'h04, read_data, "Error Type Flag"); | ||
| 111 | |||
| 112 | // 5. Heap Full Test | ||
| 113 | $display("\n[Test] Heap Full Error"); | ||
| 114 | // We used 2 cells (1 CONS). 14 cells remain. | ||
| 115 | // Loop 7 times to fill exactly to 16. | ||
| 116 | for(i=0; i<7; i=i+1) begin | ||
| 117 | cpu_write(3'h1, 8'h41); | ||
| 118 | cpu_write(3'h2, 8'h42); | ||
| 119 | cpu_write(3'h0, 8'h01); // CONS | ||
| 120 | cpu_read(3'h3, read_data); // Sync | ||
| 156 | end | 121 | end |
| 157 | 122 | ||
| 158 | // One more CONS should cause heap full error | 123 | // Try 8th CONS -> Should fail |
| 159 | cpu_write(3'h1, 8'h41); | 124 | cpu_write(3'h1, 8'hAA); |
| 160 | cpu_write(3'h2, 8'h42); | 125 | cpu_write(3'h2, 8'hBB); |
| 161 | cpu_write(3'h0, 8'h01); | 126 | cpu_write(3'h0, 8'h01); // CONS |
| 162 | |||
| 163 | cpu_read(3'h4, read_data); // Read STATUS | ||
| 164 | $display("STATUS after Heap Full: 0x%02h (expected: 0x02, ERR_HEAP_FULL=1)", read_data); | ||
| 165 | if (read_data != 8'h02) $display("ERROR: Heap Full test failed"); | ||
| 166 | 127 | ||
| 167 | $display("\nAll tests completed."); | 128 | cpu_read(3'h4, read_data); |
| 129 | // Bit 1 (ERR_HEAP) should be set -> 0x02 | ||
| 130 | assert_equals(8'h02, read_data, "Heap Full Flag"); | ||
| 131 | |||
| 132 | $display("\n--- All Tests Passed Successfully ---"); | ||
| 168 | $finish; | 133 | $finish; |
| 169 | end | 134 | end |
| 135 | |||
| 136 | // Helper task for reporting | ||
| 137 | task assert_equals(input [7:0] expected, input [7:0] actual, input string name); | ||
| 138 | if (expected !== actual) begin | ||
| 139 | $display("ERROR: %s failed. Expected 0x%h, Got 0x%h", name, expected, actual); | ||
| 140 | $finish; | ||
| 141 | end else begin | ||
| 142 | $display("PASS: %s", name); | ||
| 143 | end | ||
| 144 | endtask | ||
| 170 | 145 | ||
| 171 | endmodule | 146 | endmodule |
