Spaces:
Sleeping
Sleeping
| module tb_register_file; | |
| parameter ADDR_W = 5; | |
| parameter DATA_W = 32; | |
| reg clk, we; | |
| reg [ADDR_W-1:0] wr_addr, rd_addr_a, rd_addr_b; | |
| reg [DATA_W-1:0] wr_data; | |
| wire [DATA_W-1:0] rd_data_a, rd_data_b; | |
| integer pass_count = 0; | |
| integer fail_count = 0; | |
| register_file #(.ADDR_W(ADDR_W), .DATA_W(DATA_W)) dut ( | |
| .clk(clk), .we(we), | |
| .wr_addr(wr_addr), .wr_data(wr_data), | |
| .rd_addr_a(rd_addr_a), .rd_addr_b(rd_addr_b), | |
| .rd_data_a(rd_data_a), .rd_data_b(rd_data_b) | |
| ); | |
| always #5 clk = ~clk; | |
| task check_a; | |
| input [DATA_W-1:0] expected; | |
| input [63:0] test_id; | |
| begin | |
| if (rd_data_a === expected) begin | |
| $display("PASS: test %0d — rd_a[r%0d]=%0d", test_id, rd_addr_a, rd_data_a); | |
| pass_count = pass_count + 1; | |
| end else begin | |
| $display("FAIL: test %0d — rd_a[r%0d] got=%0d expected=%0d", | |
| test_id, rd_addr_a, rd_data_a, expected); | |
| fail_count = fail_count + 1; | |
| end | |
| end | |
| endtask | |
| task check_b; | |
| input [DATA_W-1:0] expected; | |
| input [63:0] test_id; | |
| begin | |
| if (rd_data_b === expected) begin | |
| $display("PASS: test %0d — rd_b[r%0d]=%0d", test_id, rd_addr_b, rd_data_b); | |
| pass_count = pass_count + 1; | |
| end else begin | |
| $display("FAIL: test %0d — rd_b[r%0d] got=%0d expected=%0d", | |
| test_id, rd_addr_b, rd_data_b, expected); | |
| fail_count = fail_count + 1; | |
| end | |
| end | |
| endtask | |
| initial begin | |
| clk=0; we=0; wr_addr=0; wr_data=0; rd_addr_a=0; rd_addr_b=0; | |
| // Test 1: r0 always reads 0 (before any write) | |
| rd_addr_a = 5'd0; #1; | |
| check_a(32'd0, 1); | |
| // Test 2: write r1=42, read back | |
| we=1; wr_addr=5'd1; wr_data=32'd42; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a = 5'd1; #1; | |
| check_a(32'd42, 2); | |
| // Test 3: write r0=99 — must have no effect, r0 stays 0 | |
| we=1; wr_addr=5'd0; wr_data=32'd99; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a = 5'd0; #1; | |
| check_a(32'd0, 3); | |
| // Test 4+5: simultaneous read of two different registers | |
| we=1; wr_addr=5'd2; wr_data=32'd100; | |
| @(posedge clk); #1; | |
| we=1; wr_addr=5'd3; wr_data=32'd200; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a=5'd2; rd_addr_b=5'd3; #1; | |
| check_a(32'd100, 4); | |
| check_b(32'd200, 5); | |
| // Test 6: read port B of r1 | |
| rd_addr_b=5'd1; #1; | |
| check_b(32'd42, 6); | |
| // Test 7: overwrite r1=0, verify | |
| we=1; wr_addr=5'd1; wr_data=32'd0; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a=5'd1; #1; | |
| check_a(32'd0, 7); | |
| // Test 8: large register address (r31) | |
| we=1; wr_addr=5'd31; wr_data=32'hDEADBEEF; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a=5'd31; #1; | |
| check_a(32'hDEADBEEF, 8); | |
| // Test 9: write r0 again, still reads 0 | |
| we=1; wr_addr=5'd0; wr_data=32'hFFFFFFFF; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a=5'd0; rd_addr_b=5'd0; #1; | |
| check_a(32'd0, 9); | |
| check_b(32'd0, 10); | |
| // Test 11: write MAX value to r5 | |
| we=1; wr_addr=5'd5; wr_data=32'hFFFFFFFF; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a=5'd5; #1; | |
| check_a(32'hFFFFFFFF, 11); | |
| // Test 12: two independent ports see different registers simultaneously | |
| we=1; wr_addr=5'd10; wr_data=32'd1234; | |
| @(posedge clk); #1; we=0; | |
| rd_addr_a=5'd10; rd_addr_b=5'd31; #1; | |
| check_a(32'd1234, 12); | |
| check_b(32'hDEADBEEF, 13); | |
| $display("SUMMARY: %0d passed, %0d failed", pass_count, fail_count); | |
| $finish; | |
| end | |
| initial #50000 begin | |
| $display("FAIL: timeout"); | |
| $finish; | |
| end | |
| endmodule | |