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_chip_core.v (11359B)


      1 `timescale 1ns/1ps
      2 
      3 module tb_chip_core;
      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     chip_core 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 (100MHz)
     50     initial begin
     51         clk = 0;
     52         forever #5 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         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;
     86         end
     87     endtask
     88 
     89     task check(input [7:0] expected, input [7:0] actual, input string name);
     90         if (expected !== actual) begin
     91             $display("FAIL: %s | Exp: 0x%h, Got: 0x%h", name, expected, actual);
     92             errors = errors + 1;
     93         end else begin
     94             $display("PASS: %s", name);
     95         end
     96     endtask
     97     
     98     task check_status(input bit exp_heap, input bit exp_type, input bit exp_carry, input bit exp_zero, input string name);
     99         // Status Reg: [7:5]Rsrv, [4]Zero, [3]Carry, [2]Type, [1]Heap, [0]Busy
    100         reg [7:0] expected_mask;
    101         expected_mask = {3'b000, exp_zero, exp_carry, exp_type, exp_heap, 1'b0};
    102         
    103         cpu_exec_and_read(3'h4, status_val);
    104         // Mask out the busy bit for comparison as it should be 0 now
    105         if ((status_val & 8'hFE) !== expected_mask) begin
    106             $display("FAIL: %s (Status) | Exp: %b, Got: %b", name, expected_mask, status_val);
    107             errors = errors + 1;
    108         end else begin
    109             $display("PASS: %s (Status)", name);
    110         end
    111     endtask
    112 
    113     // ========================================================================
    114     // 3. MAIN TEST SCENARIOS
    115     // ========================================================================
    116 
    117     initial begin
    118         $dumpfile("chip_core.vcd");
    119         $dumpvars(0, tb_chip_core);
    120         
    121         // --- Initialize ---
    122         rst = 1; cs = 0; rw = 0; addr = 0; data_in = 0;
    123         #20 rst = 0; #20;
    124         $display("\n=== STARTING VERIFICATION ===\n");
    125 
    126         // --------------------------------------------------------
    127         // SCENARIO 1: ALU Boundary & Overflow
    128         // --------------------------------------------------------
    129         $display("--- Scenario 1: ALU Mathematics ---");
    130         
    131         // 1.1 Simple Add: 10 + 15 = 25
    132         cpu_write(3'h1, {TAG_NUM, 6'd10}); 
    133         cpu_write(3'h2, {TAG_NUM, 6'd15}); 
    134         cpu_write(3'h0, OP_ADD);
    135         cpu_exec_and_read(3'h3, read_val);
    136         check({TAG_NUM, 6'd25}, read_val, "Add 10+15");
    137         check_status(0,0,0,0, "Add Normal Status");
    138 
    139         // 1.2 Zero Check: 0 + 0 = 0 (Should set Zero flag)
    140         cpu_write(3'h1, {TAG_NUM, 6'd0}); 
    141         cpu_write(3'h2, {TAG_NUM, 6'd0}); 
    142         cpu_write(3'h0, OP_ADD);
    143         cpu_exec_and_read(3'h3, read_val);
    144         check({TAG_NUM, 6'd0}, read_val, "Add 0+0");
    145         check_status(0,0,0,1, "Add Zero Status"); // Expect Zero=1
    146 
    147         // 1.3 Overflow Check: 63 + 1 = 0 (Should set Carry flag)
    148         // Max 6-bit unsigned is 63. 63+1 wraps to 0.
    149         cpu_write(3'h1, {TAG_NUM, 6'd63}); 
    150         cpu_write(3'h2, {TAG_NUM, 6'd1}); 
    151         cpu_write(3'h0, OP_ADD);
    152         cpu_exec_and_read(3'h3, read_val);
    153         check({TAG_NUM, 6'd0}, read_val, "Add 63+1 (Wrap)");
    154         check_status(0,0,1,1, "Add Overflow Status"); // Expect Carry=1, Zero=1
    155 
    156         // --------------------------------------------------------
    157         // SCENARIO 2: Equality (EQ) Logic
    158         // --------------------------------------------------------
    159         $display("\n--- Scenario 2: EQ Logic ---");
    160 
    161         // 2.1 Atom Equality (True)
    162         cpu_write(3'h1, VAL_A); 
    163         cpu_write(3'h2, VAL_A);
    164         cpu_write(3'h0, OP_EQ);
    165         cpu_exec_and_read(3'h3, read_val);
    166         check(VAL_TRUE, read_val, "EQ(A, A)");
    167         check_status(0,0,0,1, "EQ True Status"); // Zero flag used for equality? Spec says "ZERO (From ADD or EQ op)"
    168 
    169         // 2.2 Atom Inequality (False)
    170         cpu_write(3'h1, VAL_A); 
    171         cpu_write(3'h2, VAL_B);
    172         cpu_write(3'h0, OP_EQ);
    173         cpu_exec_and_read(3'h3, read_val);
    174         check(VAL_NIL, read_val, "EQ(A, B)");
    175         check_status(0,0,0,0, "EQ False Status");
    176 
    177         // 2.3 Mixed Type Equality (Number 10 vs Atom 10) -> Should be NIL (Bits differ in Tag)
    178         cpu_write(3'h1, {TAG_NUM, 6'd10});
    179         cpu_write(3'h2, {TAG_ATOM, 6'd10});
    180         cpu_write(3'h0, OP_EQ);
    181         cpu_exec_and_read(3'h3, read_val);
    182         check(VAL_NIL, read_val, "EQ(Num, Atom)");
    183 
    184         // --------------------------------------------------------
    185         // SCENARIO 3: Linked List Construction (Chain Verification)
    186         // --------------------------------------------------------
    187         $display("\n--- Scenario 3: Linked List (A B C) ---");
    188         // Goal: Construct (A . (B . (C . NIL)))
    189         // Steps:
    190         // 1. Node3 = CONS(C, NIL)
    191         // 2. Node2 = CONS(B, Node3)
    192         // 3. Node1 = CONS(A, Node2)
    193 
    194         // Step 1: Node 3
    195         cpu_write(3'h1, VAL_C);
    196         cpu_write(3'h2, VAL_NIL);
    197         cpu_write(3'h0, OP_CONS);
    198         cpu_exec_and_read(3'h3, ptr_node3);
    199         check({TAG_CONS, 2'b00, 4'h0}, ptr_node3, "Alloc Node 3 (Ptr=0)");
    200 
    201         // Step 2: Node 2
    202         cpu_write(3'h1, VAL_B);
    203         cpu_write(3'h2, ptr_node3);
    204         cpu_write(3'h0, OP_CONS);
    205         cpu_exec_and_read(3'h3, ptr_node2);
    206         check({TAG_CONS, 2'b00, 4'h2}, ptr_node2, "Alloc Node 2 (Ptr=2)");
    207 
    208         // Step 3: Node 1
    209         cpu_write(3'h1, VAL_A);
    210         cpu_write(3'h2, ptr_node2);
    211         cpu_write(3'h0, OP_CONS);
    212         cpu_exec_and_read(3'h3, ptr_node1);
    213         check({TAG_CONS, 2'b00, 4'h4}, ptr_node1, "Alloc Node 1 (Ptr=4)");
    214 
    215         // Step 4: Traverse! CAR(CDR(ptr_node1)) should be B
    216         
    217         // CDR(Node1) -> Should get Node2 Ptr
    218         cpu_write(3'h1, ptr_node1);
    219         cpu_write(3'h0, OP_CDR);
    220         cpu_exec_and_read(3'h3, read_val);
    221         check(ptr_node2, read_val, "Traverse: CDR(Node1)");
    222 
    223         // CAR(Result) -> Should get B
    224         cpu_write(3'h1, read_val);
    225         cpu_write(3'h0, OP_CAR);
    226         cpu_exec_and_read(3'h3, read_val);
    227         check(VAL_B, read_val, "Traverse: CAR(Node2)");
    228 
    229         // --------------------------------------------------------
    230         // SCENARIO 4: Error Type Matrix
    231         // --------------------------------------------------------
    232         $display("\n--- Scenario 4: Type Safety ---");
    233 
    234         // 4.1 CAR on ATOM (Fail)
    235         cpu_write(3'h1, VAL_A);
    236         cpu_write(3'h0, OP_CAR);
    237         check_status(0,1,0,0, "Err: CAR on Atom"); // Expect ErrType=1
    238 
    239         // 4.2 CDR on NUMBER (Fail)
    240         cpu_write(3'h1, {TAG_NUM, 6'd5});
    241         cpu_write(3'h0, OP_CDR);
    242         check_status(0,1,0,0, "Err: CDR on Number");
    243 
    244         // 4.3 ADD on CONS (Fail)
    245         cpu_write(3'h1, {TAG_NUM, 6'd5});
    246         cpu_write(3'h2, ptr_node1);
    247         cpu_write(3'h0, OP_ADD);
    248         check_status(0,1,0,0, "Err: ADD on CONS");
    249 
    250         // --------------------------------------------------------
    251         // SCENARIO 5: Heap Full Boundary
    252         // --------------------------------------------------------
    253         $display("\n--- Scenario 5: Heap Full Boundary ---");
    254         
    255         // Current Alloc Pointer is at 6 (We did 3 CONS ops: 0, 2, 4).
    256         // Capacity is 16. Addresses 6, 8, 10, 12, 14 are free.
    257         // That is 5 more CONS operations allowed.
    258         
    259         // Fill 1 (Ptr 6)
    260         cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    261         cpu_exec_and_read(3'h3, read_val); // Wait
    262 
    263         // Fill 2 (Ptr 8)
    264         cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    265         cpu_exec_and_read(3'h3, read_val);
    266 
    267         // Fill 3 (Ptr 10)
    268         cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    269         cpu_exec_and_read(3'h3, read_val);
    270 
    271         // Fill 4 (Ptr 12)
    272         cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    273         cpu_exec_and_read(3'h3, read_val);
    274 
    275         // Fill 5 (Ptr 14) - THE LAST VALID ONE
    276         cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    277         cpu_exec_and_read(3'h3, read_val);
    278         check({TAG_CONS, 2'b00, 4'hE}, read_val, "Last Valid Alloc (Ptr=14)");
    279         check_status(0,0,0,0, "Status at Capacity");
    280 
    281         // ATTEMPT OVERFLOW
    282         cpu_write(3'h1, VAL_NIL); cpu_write(3'h2, VAL_NIL); cpu_write(3'h0, OP_CONS);
    283         
    284         // Check Status
    285         cpu_exec_and_read(3'h4, status_val);
    286         // Expect ErrHeap=1
    287         if (status_val[1] !== 1'b1) begin
    288             $display("FAIL: Heap Full Detection | Got Status: %b", status_val);
    289             errors = errors + 1;
    290         end else begin
    291             $display("PASS: Heap Full Detection");
    292         end
    293 
    294         // --------------------------------------------------------
    295         // RESULT SUMMARY
    296         // --------------------------------------------------------
    297         $display("\n==================================");
    298         if (errors == 0) 
    299             $display("  SUCCESS: All Tests Passed");
    300         else 
    301             $display("  FAILURE: %0d Errors Found", errors);
    302         $display("==================================");
    303         $finish;
    304     end
    305 
    306 endmodule