tb_rv32g.sv (4254B)
1 timeunit 1ns; timeprecision 1ps; 2 3 module tb_rv32g 4 import rv32g_pkg::*; 5 (); 6 7 localparam RESULT_ADDR = 'h80004ffc; 8 9 // DUT (design-under-test) ports 10 logic clk; 11 logic rstN; 12 13 logic [31:0] imem [0:2047]; 14 logic [31:0] imem_addr; 15 logic [31:0] imem_data; 16 logic imem_valid; 17 18 logic [31:0] dmem [0:4095]; 19 logic [31:0] dmem_addr; 20 logic dmem_we; 21 logic [3:0] dmem_be; 22 logic [31:0] dmem_rdata; 23 logic [31:0] dmem_wdata; 24 logic dmem_valid; 25 26 reg_t pc_out; 27 28 exception_t exception; 29 30 string rom_filename; 31 32 // debug stuff 33 int cycle_cnt = 0; 34 35 // 100 MHz clock generation 36 // T = 1/f/2 = 1/(100*10^6)/2 = 1e-8/2 = 5ns 37 initial begin 38 clk = 0; 39 forever #5ns clk = ~clk; 40 end 41 42 initial begin 43 if (!$value$plusargs("rom=%s", rom_filename)) rom_filename = "rom.txt"; 44 $display("Reading rom from %s", rom_filename); 45 $readmemh(rom_filename, u_mem.mem); 46 47 rstN = 0; 48 imem_addr = 0; 49 dmem_addr = 0; 50 imem_valid = 1; 51 dmem_valid = 1; 52 53 #5; 54 @(posedge clk); 55 rstN = 1; 56 $display("Reset now deactivated"); 57 end 58 59 // negedge clk instead of posedge clk to avoid race-condition 60 // prints the current cycle going into the hart 61 always_ff @(negedge clk or posedge rstN) begin 62 if (!rstN) cycle_cnt <= 0; 63 else cycle_cnt <= cycle_cnt + 1; 64 $display("Cycle: %d", cycle_cnt); 65 end 66 67 unified_memory u_mem (.clk, .imem_addr, .imem_data, .imem_valid, 68 .dmem_addr, .dmem_we, .dmem_be, .dmem_valid, 69 .dmem_wdata, .dmem_rdata); 70 71 rv32g_hart dut(.clk, .rstN, .pc_out, .exception, 72 .imem_addr, .imem_data, .imem_valid, 73 .dmem_addr, .dmem_valid, 74 .dmem_rdata, .dmem_wdata, 75 .dmem_we, .dmem_be); 76 77 logic seen_first_real_inst; 78 always_ff @(posedge clk or negedge rstN) begin 79 if (!rstN) 80 seen_first_real_inst <= 0; 81 else if (dut.ifid_inst != 32'b0) 82 seen_first_real_inst <= 1; 83 end 84 85 parameter int MAX_CYCLES = 10000; 86 always_ff @(posedge clk) begin 87 $display("IF: pc: %h, next_pc: %h, imem_valid: %d, imem[%h]: %h, inst: %h", 88 dut.ifid_reg.pc, dut.next_pc, dut.imem_valid, dut.imem_addr, dut.imem_data, dut.ifid_inst); 89 $display("ID: ctrl: %h, imm: %h, rs1_val: %h, rs2_val: %h, rd_num: %d", 90 dut.idex_reg.ctrl, dut.idex_reg.imm, dut.idex_rs1_val, dut.idex_rs2_val, dut.idex_reg.rd_num); 91 $display("EX: op: %h, op1src: %d, op2src: %d, branchop: %d, out: %h, branch_taken: %d, rs1_val: %h, rs2_val: %h", 92 dut.idex_reg.ctrl.aluop, dut.idex_reg.ctrl.aluop1src, dut.idex_reg.ctrl.aluop2src, 93 dut.idex_reg.ctrl.branchop, dut.exmem_reg.alu_out, dut.exmem_reg.branch_taken, 94 dut.ex_rs1_val, dut.ex_rs2_val); 95 $display("MEM: loadop: %h, storeop: %h, dmem_addr: %h, dmem_rdata: %h, dmem_wdata: %h, dmem_we: %d, dmem_be: %b, mem_out: %h", 96 dut.exmem_reg.loadop, dut.exmem_reg.storeop, 97 dut.dmem_addr, dut.dmem_rdata, dut.dmem_wdata, dut.dmem_we, dut.dmem_be, dut.mem_mem_out); 98 $display("WB: wbsrc: %d, rd_num: %d, wb_value: %h, alu_out: %h, mem_out: %h", 99 dut.memwb_reg.wbsrc, dut.memwb_reg.rd_num, dut.memwb_reg.wb_value, 100 dut.memwb_reg.alu_out, dut.mem_mem_out); 101 102 if (dut.idex_reg.ctrl.exception != EXC_NONE) begin 103 unique case (dut.idex_reg.ctrl.exception) 104 EXC_NONE: ; 105 EXC_ILL_INST: $display("Exception: illegal instruction, inst: %h", dut.ifid_inst); 106 EXC_CALL: $display("Exception: ECALL"); 107 EXC_BKPT: $display("Exception: BREAK"); 108 endcase 109 end 110 111 // this is how I'm doing pass and fails for tests 112 if (exception == EXC_CALL) begin 113 $display("+++ %s: pass (%0d cycles) +++", rom_filename, cycle_cnt); 114 $finish(); 115 end else if (exception == EXC_BKPT) begin 116 $display("--- %s: fail (test %0d, %0d cycles) ---", rom_filename, dut.u_decode.regfile[3], cycle_cnt); 117 $finish(); 118 end 119 120 if (exception == EXC_ILL_INST && seen_first_real_inst && dut.ifid_inst == 32'b0) begin 121 $display("!!! Program terminated (exception ILL_INST at PC %h), inst: %h !!!", 122 pc_out, dut.ifid_inst); 123 $finish(); 124 end 125 126 if (cycle_cnt > MAX_CYCLES) begin 127 $display("!!! Timeout after %0d cycles !!!", MAX_CYCLES); 128 $finish(); 129 end 130 end 131 132 endmodule