From 1c7666facade946e9fd58ce779789e50994ec4a2 Mon Sep 17 00:00:00 2001 From: vin Date: Thu, 20 Nov 2025 12:01:31 -0500 Subject: add more comprehensive test bench --- rtl/tb_lisp_coproc.sv | 348 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 254 insertions(+), 94 deletions(-) (limited to 'rtl') diff --git a/rtl/tb_lisp_coproc.sv b/rtl/tb_lisp_coproc.sv index aa721fc..08e3627 100644 --- 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 -- cgit v1.2.3