diff options
| author | vin <git@vineetk.net> | 2025-12-08 22:19:25 -0500 |
|---|---|---|
| committer | vin <git@vineetk.net> | 2025-12-08 22:19:40 -0500 |
| commit | 36f4ec74c5d1247362bed901dbaf0a36970b4cd0 (patch) | |
| tree | bd460ccc65a86ec26e668c4e1a606fc70c4023fa /vlsi/tb_chip_core.v | |
| parent | 95997aac1be995dce6bc6fdb01a2880242700e8f (diff) | |
final changes from electric before moving to librelane
This is the commit used for my class's final report. Now I'm free to
use whatever tool I want. Cadence/Synopsys seems to be much more
automated compared to Electric, and LibreLane (previously OpenLane)
seems to be more closely resemble that.
Diffstat (limited to 'vlsi/tb_chip_core.v')
| -rw-r--r-- | vlsi/tb_chip_core.v | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/vlsi/tb_chip_core.v b/vlsi/tb_chip_core.v new file mode 100644 index 0000000..66fa212 --- /dev/null +++ b/vlsi/tb_chip_core.v | |||
| @@ -0,0 +1,306 @@ | |||
| 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_robust.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 ROBUST 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 | ||
