`timescale 1 ns / 1 ns
module regfile_tb();
parameter Td2 = 5; // the half of the period
parameter Nd = 16; // the data size
parameter Na =  3; // the address size
parameter Nreg = (1 << Na); // the number of registers
reg clk, rst_n, we;
wire [Nd-1:0] rda, rdb; // read data
reg [Nd-1:0] d;         // write data
reg [Na-1:0] wa, ra, rb; // addresses
reg [Nd-1:0] regm [0:Nreg-1]; // shadow memory for the regs
integer i; // loop index
regfile dut(.clk(clk), .rst_n(rst_n), .we(we), .din(d), .waddr(wa),
             .raddra(ra), .raddrb(rb), .rdata(rda), .rdatb(rdb));
defparam dut.Nd = Nd; // map the parameters
defparam dut.Na = Na;
always # Td2 clk =  ~clk; // generate the clock
initial begin
    $dumpfile("regfile.vcd");
    $dumpvars(1, regfile_tb.dut);
  end
initial
   $monitor("Zeit: %t, write enable changed to: %b", $time, we);
initial
begin
  clk = 1'b0;   // initial value of the clock
  rst_n = 0;    // power up reset
  repeat (2) @(negedge clk);
  rst_n = 1;    // deactivate the reset
  for (i = 0; i < Nreg; i = i + 1) // write some pattern
    write_reg(i, i | (i+1) << 8);
  for (i = 0; i < Nreg; i = i + 1) // read back
    read_reg(0, i, regm[i]);       // using port A
  for (i = 0; i < Nreg; i = i + 1)
    read_reg(1, i, regm[i]);       // and port B
  rst_n = 0;                       // reset
  @(negedge clk);
  rst_n = 1;
  for (i = 0; i < Nreg; i = i + 1) // read and expect 0
    read_reg(0, i, 0);
  for (i = 0; i < Nreg; i = i + 1) // from both ports
    read_reg(1, i, 0);
end
task write_reg;
  input [Na-1:0] rega;
  input [Nd-1:0] regd;
  begin
    we = 1;
    d  = regd;
    wa = rega;
    @ (negedge clk);
    regm[rega] = regd;
    we = 0;
  end
endtask
task read_reg;
  input prt; // 0 or 1
  input [Na-1:0] rega;
  input [Nd-1:0] regd;
  begin
    we = 0;
    if (prt==0) ra = rega;
    else        rb = rega;
    @ (negedge clk);
    if ((prt==0) && (rda != regd))
      $display("%t : expected 0x%04x, readA 0x%04x, reg# %d", $time, regd, rda, rega);
    if ((prt==1) && (rdb != regd))
      $display("%t : expected 0x%04x, readB 0x%04x, reg# %d", $time, regd, rdb, rega);
  end
endtask
endmodule