rv32g-core

Pipelined, synthesizable, and fabricable RISC-V core in SystemVerilog+Yosys+OpenROAD
Log | Files | Refs | LICENSE

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