summaryrefslogtreecommitdiff
path: root/rtl
diff options
context:
space:
mode:
authorvin <git@vineetk.net>2025-12-10 16:35:42 -0500
committervin <git@vineetk.net>2025-12-10 16:39:36 -0500
commit06b6b116934cc8341676f52f5f6040a038020c32 (patch)
treef2db3531293b6de1a67a1e26a2aa1663f4d55082 /rtl
parent36f4ec74c5d1247362bed901dbaf0a36970b4cd0 (diff)
migrate from electric vlsi to librelane/sky130 flowHEADmaster
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.sv453
-rw-r--r--rtl/tb_lisp_coproc.sv611
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
3module lisp_coproc ( 1module 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
220endmodule 251endmodule
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
3module tb_lisp_coproc; 3module 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
306endmodule 323endmodule