cda4210_lisp_coproc

A primitive Lisp accelerator in a full-custom physical design (Electric VLSI MOSIS 350nm) and RTL digital design (Librelane Sky130nm PDK)
Log | Files | Refs

tb_lisp_coproc.sv (11454B)


      1 `timescale 1ns/1ps
      2 
      3 module tb_lisp_coproc;
      4 
      5    // ========================================================================
      6    // 1. CONFIGURATION & CONSTANTS
      7    // ========================================================================
      8 
      9    // Opcodes
     10    localparam [7:0] OP_CONS = 8'h01;
     11    localparam [7:0] OP_CAR  = 8'h02;
     12    localparam [7:0] OP_CDR  = 8'h03;
     13    localparam [7:0] OP_ATOM = 8'h04;
     14    localparam [7:0] OP_EQ   = 8'h05;
     15    localparam [7:0] OP_ADD  = 8'h06;
     16 
     17    // Tags
     18    localparam [1:0] TAG_NIL  = 2'b00;
     19    localparam [1:0] TAG_ATOM = 2'b01;
     20    localparam [1:0] TAG_NUM  = 2'b10;
     21    localparam [1:0] TAG_CONS = 2'b11;
     22 
     23    // Standard Values for Testing
     24    localparam [7:0] VAL_NIL  = {TAG_NIL,  6'h00};
     25    localparam [7:0] VAL_TRUE = {TAG_ATOM, 6'h01}; // 'T'
     26    localparam [7:0] VAL_A    = {TAG_ATOM, 6'h0A};
     27    localparam [7:0] VAL_B    = {TAG_ATOM, 6'h0B};
     28    localparam [7:0] VAL_C    = {TAG_ATOM, 6'h0C};
     29 
     30    // Testbench Signals
     31    reg		    clk, rst, cs, rw;
     32    reg [2:0]	    addr;
     33    reg [7:0]	    data_in;
     34    wire [7:0]	    data_out;
     35    
     36    // Verification Variables
     37    reg [7:0]	    read_val;
     38    reg [7:0]	    status_val;
     39    reg [7:0]	    ptr_node3, ptr_node2, ptr_node1;
     40    integer	    errors = 0;
     41    integer	    i;
     42 
     43    // Instantiate DUT
     44    lisp_coproc dut (
     45 		    .clk(clk), .rst(rst), .cs(cs), .rw(rw),
     46 		    .addr(addr), .data_in(data_in), .data_out(data_out)
     47 		    );
     48 
     49    // Clock Generation (10MHz)
     50    initial begin
     51       clk = 0;
     52       forever #50 clk = ~clk;
     53    end
     54 
     55    // ========================================================================
     56    // 2. HELPER TASKS
     57    // ========================================================================
     58 
     59    task cpu_write(input [2:0] w_addr, input [7:0] w_data);
     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
     75             @(posedge clk);
     76             cs = 1; rw = 1; addr = 3'h4; // Status
     77             @(posedge clk);
     78             status_val = data_out;
     79             cs = 0;
     80             
     81             // Panic button: Break if stuck for 100 cycles
     82             timeout = timeout + 1;
     83             if (timeout > 100) begin
     84                $display("ERROR: Timed out polling BUSY bit! Status: %b", status_val);
     85                break; 
     86             end
     87          end while (status_val[0] === 1'b1);
     88 
     89          // Perform Read
     90          @(posedge clk);
     91          cs = 1;
     92          rw = 1; addr = r_addr;
     93          @(posedge clk);
     94          r_data = data_out;
     95          cs = 0;
     96       end
     97    endtask
     98 
     99    task check(input [7:0] expected, input [7:0] actual, input string name);
    100       if (expected !== actual) begin
    101          $display("FAIL: %s | Exp: 0x%h, Got: 0x%h", name, expected, actual);
    102          errors = errors + 1;
    103       end else begin
    104          $display("PASS: %s", name);
    105       end
    106    endtask
    107    
    108    task check_status(input bit exp_heap, input bit exp_type, input bit exp_carry, input bit exp_zero, input string name);
    109       // Status Reg: [7:5]Rsrv, [4]Zero, [3]Carry, [2]Type, [1]Heap, [0]Busy
    110       reg [7:0] expected_mask;
    111       expected_mask = {3'b000, exp_zero, exp_carry, exp_type, exp_heap, 1'b0};
    112       
    113       cpu_exec_and_read(3'h4, status_val);
    114       // Mask out the busy bit for comparison as it should be 0 now
    115       if ((status_val & 8'hFE) !== expected_mask) begin
    116          $display("FAIL: %s (Status) | Exp: %b, Got: %b", name, expected_mask, status_val);
    117          errors = errors + 1;
    118       end else begin
    119          $display("PASS: %s (Status)", name);
    120       end
    121    endtask
    122 
    123    // ========================================================================
    124    // 3. MAIN TEST SCENARIOS
    125    // ========================================================================
    126 
    127    initial begin
    128       $dumpfile("lisp_coproc.vcd");
    129       $dumpvars(0, tb_lisp_coproc);
    130       
    131       // --- Initialize ---
    132       clk = 0;
    133       cs = 0; rw = 0; addr = 0; data_in = 0;
    134       
    135       // --- AGGRESSIVE RESET SEQUENCE ---
    136       rst = 1;          // Assert Reset
    137       #500;             // Hold for 50 cycles (allows X propagation to clear)
    138       rst = 0;          // Release Reset
    139       #100;             // Wait for logic to settle into IDLE
    140       
    141       $display("\n=== STARTING VERIFICATION ===\n");
    142 
    143       // --------------------------------------------------------
    144       // SCENARIO 1: ALU Boundary & Overflow
    145       // --------------------------------------------------------
    146       $display("--- Scenario 1: ALU Mathematics ---");
    147       
    148       // 1.1 Simple Add: 10 + 15 = 25
    149       cpu_write(3'h1, {TAG_NUM, 6'd10}); 
    150       cpu_write(3'h2, {TAG_NUM, 6'd15}); 
    151       cpu_write(3'h0, OP_ADD);
    152       cpu_exec_and_read(3'h3, read_val);
    153       check({TAG_NUM, 6'd25}, read_val, "Add 10+15");
    154       check_status(0,0,0,0, "Add Normal Status");
    155 
    156       // 1.2 Zero Check: 0 + 0 = 0 (Should set Zero flag)
    157       cpu_write(3'h1, {TAG_NUM, 6'd0}); 
    158       cpu_write(3'h2, {TAG_NUM, 6'd0}); 
    159       cpu_write(3'h0, OP_ADD);
    160       cpu_exec_and_read(3'h3, read_val);
    161       check({TAG_NUM, 6'd0}, read_val, "Add 0+0");
    162       check_status(0,0,0,1, "Add Zero Status"); // Expect Zero=1
    163 
    164       // 1.3 Overflow Check: 63 + 1 = 0 (Should set Carry flag)
    165       // Max 6-bit unsigned is 63. 63+1 wraps to 0.
    166       cpu_write(3'h1, {TAG_NUM, 6'd63}); 
    167       cpu_write(3'h2, {TAG_NUM, 6'd1}); 
    168       cpu_write(3'h0, OP_ADD);
    169       cpu_exec_and_read(3'h3, read_val);
    170       check({TAG_NUM, 6'd0}, read_val, "Add 63+1 (Wrap)");
    171       check_status(0,0,1,1, "Add Overflow Status"); // Expect Carry=1, Zero=1
    172 
    173       // --------------------------------------------------------
    174       // SCENARIO 2: Equality (EQ) Logic
    175       // --------------------------------------------------------
    176       $display("\n--- Scenario 2: EQ Logic ---");
    177 
    178       // 2.1 Atom Equality (True)
    179       cpu_write(3'h1, VAL_A); 
    180       cpu_write(3'h2, VAL_A);
    181       cpu_write(3'h0, OP_EQ);
    182       cpu_exec_and_read(3'h3, read_val);
    183       check(VAL_TRUE, read_val, "EQ(A, A)");
    184       check_status(0,0,0,1, "EQ True Status"); // Zero flag used for equality? Spec says "ZERO (From ADD or EQ op)"
    185 
    186       // 2.2 Atom Inequality (False)
    187       cpu_write(3'h1, VAL_A); 
    188       cpu_write(3'h2, VAL_B);
    189       cpu_write(3'h0, OP_EQ);
    190       cpu_exec_and_read(3'h3, read_val);
    191       check(VAL_NIL, read_val, "EQ(A, B)");
    192       check_status(0,0,0,0, "EQ False Status");
    193 
    194       // 2.3 Mixed Type Equality (Number 10 vs Atom 10) -> Should be NIL (Bits differ in Tag)
    195       cpu_write(3'h1, {TAG_NUM, 6'd10});
    196       cpu_write(3'h2, {TAG_ATOM, 6'd10});
    197       cpu_write(3'h0, OP_EQ);
    198       cpu_exec_and_read(3'h3, read_val);
    199       check(VAL_NIL, read_val, "EQ(Num, Atom)");
    200 
    201       // --------------------------------------------------------
    202       // SCENARIO 3: Linked List Construction (Chain Verification)
    203       // --------------------------------------------------------
    204       $display("\n--- Scenario 3: Linked List (A B C) ---");
    205       // Goal: Construct (A . (B . (C . NIL)))
    206       // Steps:
    207       // 1. Node3 = CONS(C, NIL)
    208       // 2. Node2 = CONS(B, Node3)
    209       // 3. Node1 = CONS(A, Node2)
    210 
    211       // Step 1: Node 3
    212       cpu_write(3'h1, VAL_C);
    213       cpu_write(3'h2, VAL_NIL);
    214       cpu_write(3'h0, OP_CONS);
    215       cpu_exec_and_read(3'h3, ptr_node3);
    216       check({TAG_CONS, 2'b00, 4'h0}, ptr_node3, "Alloc Node 3 (Ptr=0)");
    217 
    218       // Step 2: Node 2
    219       cpu_write(3'h1, VAL_B);
    220       cpu_write(3'h2, ptr_node3);
    221       cpu_write(3'h0, OP_CONS);
    222       cpu_exec_and_read(3'h3, ptr_node2);
    223       check({TAG_CONS, 2'b00, 4'h2}, ptr_node2, "Alloc Node 2 (Ptr=2)");
    224 
    225       // Step 3: Node 1
    226       cpu_write(3'h1, VAL_A);
    227       cpu_write(3'h2, ptr_node2);
    228       cpu_write(3'h0, OP_CONS);
    229       cpu_exec_and_read(3'h3, ptr_node1);
    230       check({TAG_CONS, 2'b00, 4'h4}, ptr_node1, "Alloc Node 1 (Ptr=4)");
    231 
    232       // Step 4: Traverse! CAR(CDR(ptr_node1)) should be B
    233       
    234       // CDR(Node1) -> Should get Node2 Ptr
    235       cpu_write(3'h1, ptr_node1);
    236       cpu_write(3'h0, OP_CDR);
    237       cpu_exec_and_read(3'h3, read_val);
    238       check(ptr_node2, read_val, "Traverse: CDR(Node1)");
    239 
    240       // CAR(Result) -> Should get B
    241       cpu_write(3'h1, read_val);
    242       cpu_write(3'h0, OP_CAR);
    243       cpu_exec_and_read(3'h3, read_val);
    244       check(VAL_B, read_val, "Traverse: CAR(Node2)");
    245 
    246       // --------------------------------------------------------
    247       // SCENARIO 4: Error Type Matrix
    248       // --------------------------------------------------------
    249       $display("\n--- Scenario 4: Type Safety ---");
    250 
    251       // 4.1 CAR on ATOM (Fail)
    252       cpu_write(3'h1, VAL_A);
    253       cpu_write(3'h0, OP_CAR);
    254       check_status(0,1,0,0, "Err: CAR on Atom"); // Expect ErrType=1
    255 
    256       // 4.2 CDR on NUMBER (Fail)
    257       cpu_write(3'h1, {TAG_NUM, 6'd5});
    258       cpu_write(3'h0, OP_CDR);
    259       check_status(0,1,0,0, "Err: CDR on Number");
    260 
    261       // 4.3 ADD on CONS (Fail)
    262       cpu_write(3'h1, {TAG_NUM, 6'd5});
    263       cpu_write(3'h2, ptr_node1);
    264       cpu_write(3'h0, OP_ADD);
    265       check_status(0,1,0,0, "Err: ADD on CONS");
    266 
    267       // --------------------------------------------------------
    268       // SCENARIO 5: Heap Full Boundary
    269       // --------------------------------------------------------
    270       $display("\n--- Scenario 5: Heap Full Boundary ---");
    271       
    272       // Current Alloc Pointer is at 6 (We did 3 CONS ops: 0, 2, 4).
    273       // Capacity is 16. Addresses 6, 8, 10, 12, 14 are free.
    274       // That is 5 more CONS operations allowed.
    275       
    276       // Fill 1 (Ptr 6)
    277       cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    278       cpu_exec_and_read(3'h3, read_val); // Wait
    279 
    280       // Fill 2 (Ptr 8)
    281       cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    282       cpu_exec_and_read(3'h3, read_val);
    283 
    284       // Fill 3 (Ptr 10)
    285       cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    286       cpu_exec_and_read(3'h3, read_val);
    287 
    288       // Fill 4 (Ptr 12)
    289       cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    290       cpu_exec_and_read(3'h3, read_val);
    291 
    292       // Fill 5 (Ptr 14) - THE LAST VALID ONE
    293       cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    294       cpu_exec_and_read(3'h3, read_val);
    295       check({TAG_CONS, 2'b00, 4'hE}, read_val, "Last Valid Alloc (Ptr=14)");
    296       check_status(0,0,0,0, "Status at Capacity");
    297 
    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
    322 
    323 endmodule