commit 1c7666facade946e9fd58ce779789e50994ec4a2
parent 46303e07e9bda309cb451aa824680db9bdd19fe8
Author: vin <git@vineetk.net>
Date: Thu, 20 Nov 2025 12:01:31 -0500
add more comprehensive test bench
Diffstat:
| M | rtl/tb_lisp_coproc.sv | | | 348 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
1 file changed, 254 insertions(+), 94 deletions(-)
diff --git a/rtl/tb_lisp_coproc.sv b/rtl/tb_lisp_coproc.sv
@@ -2,52 +2,82 @@
module tb_lisp_coproc;
- // Signals
+ // ========================================================================
+ // 1. CONFIGURATION & CONSTANTS
+ // ========================================================================
+
+ // Opcodes
+ localparam [7:0] OP_CONS = 8'h01;
+ localparam [7:0] OP_CAR = 8'h02;
+ localparam [7:0] OP_CDR = 8'h03;
+ localparam [7:0] OP_ATOM = 8'h04;
+ localparam [7:0] OP_EQ = 8'h05;
+ localparam [7:0] OP_ADD = 8'h06;
+
+ // Tags
+ localparam [1:0] TAG_NIL = 2'b00;
+ localparam [1:0] TAG_ATOM = 2'b01;
+ localparam [1:0] TAG_NUM = 2'b10;
+ localparam [1:0] TAG_CONS = 2'b11;
+
+ // Standard Values for Testing
+ localparam [7:0] VAL_NIL = {TAG_NIL, 6'h00};
+ localparam [7:0] VAL_TRUE = {TAG_ATOM, 6'h01}; // 'T'
+ localparam [7:0] VAL_A = {TAG_ATOM, 6'h0A};
+ localparam [7:0] VAL_B = {TAG_ATOM, 6'h0B};
+ localparam [7:0] VAL_C = {TAG_ATOM, 6'h0C};
+
+ // Testbench Signals
reg clk, rst, cs, rw;
reg [2:0] addr;
reg [7:0] data_in;
wire [7:0] data_out;
- // Verification Vars
- reg [7:0] read_data;
+ // Verification Variables
+ reg [7:0] read_val;
+ reg [7:0] status_val;
+ reg [7:0] ptr_node3, ptr_node2, ptr_node1;
+ integer errors = 0;
integer i;
-
- // DUT Instance
+
+ // Instantiate DUT
lisp_coproc dut (
.clk(clk), .rst(rst), .cs(cs), .rw(rw),
.addr(addr), .data_in(data_in), .data_out(data_out)
);
-
- // 100MHz Clock
+
+ // Clock Generation (100MHz)
initial begin
clk = 0;
forever #5 clk = ~clk;
end
-
- // --- CPU Bus Tasks ---
-
+
+ // ========================================================================
+ // 2. HELPER TASKS
+ // ========================================================================
+
task cpu_write(input [2:0] w_addr, input [7:0] w_data);
begin
@(posedge clk);
cs = 1; rw = 0; addr = w_addr; data_in = w_data;
@(posedge clk);
- cs = 0; data_in = 8'h00; // Clear bus
+ cs = 0; data_in = 8'h00;
end
endtask
- task cpu_read(input [2:0] r_addr, output [7:0] r_data);
- reg [7:0] stat;
+ // Read with auto-polling for BUSY flag
+ task cpu_exec_and_read(input [2:0] r_addr, output [7:0] r_data);
begin
- // 1. Poll Status for BUSY=0
+ // Poll Status Bit 0 (BUSY)
do begin
@(posedge clk);
- cs = 1; rw = 1; addr = 3'h4; // Status Reg
+ cs = 1; rw = 1; addr = 3'h4; // Status
@(posedge clk);
- stat = data_out;
+ status_val = data_out;
cs = 0;
- end while (stat[0] == 1'b1);
+ end while (status_val[0] === 1'b1);
- // 2. Perform Actual Read
+ // Perform Read
@(posedge clk);
cs = 1; rw = 1; addr = r_addr;
@(posedge clk);
@@ -55,92 +85,222 @@ module tb_lisp_coproc;
cs = 0;
end
endtask
+
+ task check(input [7:0] expected, input [7:0] actual, input string name);
+ if (expected !== actual) begin
+ $display("FAIL: %s | Exp: 0x%h, Got: 0x%h", name, expected, actual);
+ errors = errors + 1;
+ end else begin
+ $display("PASS: %s", name);
+ end
+ endtask
- // --- Main Test Sequence ---
+ task check_status(input bit exp_heap, input bit exp_type, input bit exp_carry, input bit exp_zero, input string name);
+ // Status Reg: [7:5]Rsrv, [4]Zero, [3]Carry, [2]Type, [1]Heap, [0]Busy
+ reg [7:0] expected_mask;
+ expected_mask = {3'b000, exp_zero, exp_carry, exp_type, exp_heap, 1'b0};
+
+ cpu_exec_and_read(3'h4, status_val);
+ // Mask out the busy bit for comparison as it should be 0 now
+ if ((status_val & 8'hFE) !== expected_mask) begin
+ $display("FAIL: %s (Status) | Exp: %b, Got: %b", name, expected_mask, status_val);
+ errors = errors + 1;
+ end else begin
+ $display("PASS: %s (Status)", name);
+ end
+ endtask
+
+ // ========================================================================
+ // 3. MAIN TEST SCENARIOS
+ // ========================================================================
+
initial begin
- $dumpfile("lisp_coproc.vcd");
+ $dumpfile("lisp_coproc_robust.vcd");
$dumpvars(0, tb_lisp_coproc);
-
- // Init
+
+ // --- Initialize ---
rst = 1; cs = 0; rw = 0; addr = 0; data_in = 0;
#20 rst = 0; #20;
+ $display("\n=== STARTING ROBUST VERIFICATION ===\n");
+
+ // --------------------------------------------------------
+ // SCENARIO 1: ALU Boundary & Overflow
+ // --------------------------------------------------------
+ $display("--- Scenario 1: ALU Mathematics ---");
- $display("--- Starting Lisp Coprocessor Verification ---");
-
- // 1. Reset Test
- cpu_read(3'h4, read_data);
- assert_equals(8'h00, read_data, "Reset Status");
-
- // 2. ADD Test (5 + 6 = 11)
- $display("\n[Test] ADD Operation");
- cpu_write(3'h1, 8'h85); // Number(5)
- cpu_write(3'h2, 8'h86); // Number(6)
- cpu_write(3'h0, 8'h06); // OPCODE: ADD
- cpu_read(3'h3, read_data);
- assert_equals(8'h8B, read_data, "ADD Result (11)");
- cpu_read(3'h4, read_data);
- assert_equals(8'h00, read_data, "ADD Status (Clean)");
-
- // 3. CONS Test
- $display("\n[Test] CONS Operation");
- cpu_write(3'h1, 8'h41); // Atom('T')
- cpu_write(3'h2, 8'h42); // Atom('B')
- cpu_write(3'h0, 8'h01); // OPCODE: CONS
- cpu_read(3'h3, read_data);
- // Expect CONS Tag (11) | Address (0000) -> 0xC0
- assert_equals(8'hC0, read_data, "CONS Pointer");
-
- // Verify Heap Persistence via CAR/CDR
- cpu_write(3'h1, 8'hC0); // Pointer to just allocated cell
- cpu_write(3'h0, 8'h02); // OPCODE: CAR
- cpu_read(3'h3, read_data);
- assert_equals(8'h41, read_data, "CAR Check");
-
- cpu_write(3'h1, 8'hC0);
- cpu_write(3'h0, 8'h03); // OPCODE: CDR
- cpu_read(3'h3, read_data);
- assert_equals(8'h42, read_data, "CDR Check");
-
- // 4. Error Test (Type Error)
- $display("\n[Test] Error Handling (Type)");
- cpu_write(3'h1, 8'h85); // Number
- cpu_write(3'h0, 8'h02); // OPCODE: CAR (Invalid on Number)
- cpu_read(3'h4, read_data);
- // Bit 2 (ERR_TYPE) should be set -> 0x04
- assert_equals(8'h04, read_data, "Error Type Flag");
-
- // 5. Heap Full Test
- $display("\n[Test] Heap Full Error");
- // We used 2 cells (1 CONS). 14 cells remain.
- // Loop 7 times to fill exactly to 16.
- for(i=0; i<7; i=i+1) begin
- cpu_write(3'h1, 8'h41);
- cpu_write(3'h2, 8'h42);
- cpu_write(3'h0, 8'h01); // CONS
- cpu_read(3'h3, read_data); // Sync
- end
+ // 1.1 Simple Add: 10 + 15 = 25
+ cpu_write(3'h1, {TAG_NUM, 6'd10});
+ cpu_write(3'h2, {TAG_NUM, 6'd15});
+ cpu_write(3'h0, OP_ADD);
+ cpu_exec_and_read(3'h3, read_val);
+ check({TAG_NUM, 6'd25}, read_val, "Add 10+15");
+ check_status(0,0,0,0, "Add Normal Status");
+
+ // 1.2 Zero Check: 0 + 0 = 0 (Should set Zero flag)
+ cpu_write(3'h1, {TAG_NUM, 6'd0});
+ cpu_write(3'h2, {TAG_NUM, 6'd0});
+ cpu_write(3'h0, OP_ADD);
+ cpu_exec_and_read(3'h3, read_val);
+ check({TAG_NUM, 6'd0}, read_val, "Add 0+0");
+ check_status(0,0,0,1, "Add Zero Status"); // Expect Zero=1
+
+ // 1.3 Overflow Check: 63 + 1 = 0 (Should set Carry flag)
+ // Max 6-bit unsigned is 63. 63+1 wraps to 0.
+ cpu_write(3'h1, {TAG_NUM, 6'd63});
+ cpu_write(3'h2, {TAG_NUM, 6'd1});
+ cpu_write(3'h0, OP_ADD);
+ cpu_exec_and_read(3'h3, read_val);
+ check({TAG_NUM, 6'd0}, read_val, "Add 63+1 (Wrap)");
+ check_status(0,0,1,1, "Add Overflow Status"); // Expect Carry=1, Zero=1
+
+ // --------------------------------------------------------
+ // SCENARIO 2: Equality (EQ) Logic
+ // --------------------------------------------------------
+ $display("\n--- Scenario 2: EQ Logic ---");
+
+ // 2.1 Atom Equality (True)
+ cpu_write(3'h1, VAL_A);
+ cpu_write(3'h2, VAL_A);
+ cpu_write(3'h0, OP_EQ);
+ cpu_exec_and_read(3'h3, read_val);
+ check(VAL_TRUE, read_val, "EQ(A, A)");
+ check_status(0,0,0,1, "EQ True Status"); // Zero flag used for equality? Spec says "ZERO (From ADD or EQ op)"
+
+ // 2.2 Atom Inequality (False)
+ cpu_write(3'h1, VAL_A);
+ cpu_write(3'h2, VAL_B);
+ cpu_write(3'h0, OP_EQ);
+ cpu_exec_and_read(3'h3, read_val);
+ check(VAL_NIL, read_val, "EQ(A, B)");
+ check_status(0,0,0,0, "EQ False Status");
+
+ // 2.3 Mixed Type Equality (Number 10 vs Atom 10) -> Should be NIL (Bits differ in Tag)
+ cpu_write(3'h1, {TAG_NUM, 6'd10});
+ cpu_write(3'h2, {TAG_ATOM, 6'd10});
+ cpu_write(3'h0, OP_EQ);
+ cpu_exec_and_read(3'h3, read_val);
+ check(VAL_NIL, read_val, "EQ(Num, Atom)");
+
+ // --------------------------------------------------------
+ // SCENARIO 3: Linked List Construction (Chain Verification)
+ // --------------------------------------------------------
+ $display("\n--- Scenario 3: Linked List (A B C) ---");
+ // Goal: Construct (A . (B . (C . NIL)))
+ // Steps:
+ // 1. Node3 = CONS(C, NIL)
+ // 2. Node2 = CONS(B, Node3)
+ // 3. Node1 = CONS(A, Node2)
+
+ // Step 1: Node 3
+ cpu_write(3'h1, VAL_C);
+ cpu_write(3'h2, VAL_NIL);
+ cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, ptr_node3);
+ check({TAG_CONS, 2'b00, 4'h0}, ptr_node3, "Alloc Node 3 (Ptr=0)");
+
+ // Step 2: Node 2
+ cpu_write(3'h1, VAL_B);
+ cpu_write(3'h2, ptr_node3);
+ cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, ptr_node2);
+ check({TAG_CONS, 2'b00, 4'h2}, ptr_node2, "Alloc Node 2 (Ptr=2)");
+
+ // Step 3: Node 1
+ cpu_write(3'h1, VAL_A);
+ cpu_write(3'h2, ptr_node2);
+ cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, ptr_node1);
+ check({TAG_CONS, 2'b00, 4'h4}, ptr_node1, "Alloc Node 1 (Ptr=4)");
+
+ // Step 4: Traverse! CAR(CDR(ptr_node1)) should be B
+
+ // CDR(Node1) -> Should get Node2 Ptr
+ cpu_write(3'h1, ptr_node1);
+ cpu_write(3'h0, OP_CDR);
+ cpu_exec_and_read(3'h3, read_val);
+ check(ptr_node2, read_val, "Traverse: CDR(Node1)");
+
+ // CAR(Result) -> Should get B
+ cpu_write(3'h1, read_val);
+ cpu_write(3'h0, OP_CAR);
+ cpu_exec_and_read(3'h3, read_val);
+ check(VAL_B, read_val, "Traverse: CAR(Node2)");
+
+ // --------------------------------------------------------
+ // SCENARIO 4: Error Type Matrix
+ // --------------------------------------------------------
+ $display("\n--- Scenario 4: Type Safety ---");
+
+ // 4.1 CAR on ATOM (Fail)
+ cpu_write(3'h1, VAL_A);
+ cpu_write(3'h0, OP_CAR);
+ check_status(0,1,0,0, "Err: CAR on Atom"); // Expect ErrType=1
+
+ // 4.2 CDR on NUMBER (Fail)
+ cpu_write(3'h1, {TAG_NUM, 6'd5});
+ cpu_write(3'h0, OP_CDR);
+ check_status(0,1,0,0, "Err: CDR on Number");
+
+ // 4.3 ADD on CONS (Fail)
+ cpu_write(3'h1, {TAG_NUM, 6'd5});
+ cpu_write(3'h2, ptr_node1);
+ cpu_write(3'h0, OP_ADD);
+ check_status(0,1,0,0, "Err: ADD on CONS");
+
+ // --------------------------------------------------------
+ // SCENARIO 5: Heap Full Boundary
+ // --------------------------------------------------------
+ $display("\n--- Scenario 5: Heap Full Boundary ---");
- // Try 8th CONS -> Should fail
- cpu_write(3'h1, 8'hAA);
- cpu_write(3'h2, 8'hBB);
- cpu_write(3'h0, 8'h01); // CONS
+ // Current Alloc Pointer is at 6 (We did 3 CONS ops: 0, 2, 4).
+ // Capacity is 16. Addresses 6, 8, 10, 12, 14 are free.
+ // That is 5 more CONS operations allowed.
- cpu_read(3'h4, read_data);
- // Bit 1 (ERR_HEAP) should be set -> 0x02
- assert_equals(8'h02, read_data, "Heap Full Flag");
+ // Fill 1 (Ptr 6)
+ cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, read_val); // Wait
- $display("\n--- All Tests Passed Successfully ---");
- $finish;
- end
-
- // Helper task for reporting
- task assert_equals(input [7:0] expected, input [7:0] actual, input string name);
- if (expected !== actual) begin
- $display("ERROR: %s failed. Expected 0x%h, Got 0x%h", name, expected, actual);
- $finish;
+ // Fill 2 (Ptr 8)
+ cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, read_val);
+
+ // Fill 3 (Ptr 10)
+ cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, read_val);
+
+ // Fill 4 (Ptr 12)
+ cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, read_val);
+
+ // Fill 5 (Ptr 14) - THE LAST VALID ONE
+ cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
+ cpu_exec_and_read(3'h3, read_val);
+ check({TAG_CONS, 2'b00, 4'hE}, read_val, "Last Valid Alloc (Ptr=14)");
+ check_status(0,0,0,0, "Status at Capacity");
+
+ // ATTEMPT OVERFLOW
+ cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
+
+ // Check Status
+ cpu_exec_and_read(3'h4, status_val);
+ // Expect ErrHeap=1
+ if (status_val[1] !== 1'b1) begin
+ $display("FAIL: Heap Full Detection | Got Status: %b", status_val);
+ errors = errors + 1;
end else begin
- $display("PASS: %s", name);
+ $display("PASS: Heap Full Detection");
end
- endtask
+
+ // --------------------------------------------------------
+ // RESULT SUMMARY
+ // --------------------------------------------------------
+ $display("\n==================================");
+ if (errors == 0)
+ $display(" SUCCESS: All Tests Passed");
+ else
+ $display(" FAILURE: %0d Errors Found", errors);
+ $display("==================================");
+ $finish;
+ end
endmodule