/// LSU EE 3755 -- Spring 2002 -- Computer Organization
//
/// Testbench for MIPS Functional Simulator
//
//   Does not actually test, just runs it.
//
// Time-stamp: <24 April 2002, 12:03:47 CDT, koppel@sol>

// exemplar translate_off

`ifdef MEMBASE
`else
`define MEMBASE 'h400000
`define DATABASE 'h10010000
`define TEXTSIZE 'h100
`define MEMSIZE 'h200
`define MEMRANGE `MEMBASE:`MEMBASE+`MEMSIZE-1
`define A(addr) ((addr)-`DATABASE+`MEMBASE+`TEXTSIZE)
`define MEM(addr) mem[((addr)-`DATABASE+`MEMBASE+`TEXTSIZE)]
`endif

module test_proc();

   wire exception;
   reg  clk;
   integer cycle_count;
   parameter cycle_limit = 20;

   proc dut(exception,clk);

   reg [31:0] gpr_shadow [0:31];
   reg [319:0] isource[`MEMBASE>>2:((`MEMBASE+`MEMSIZE)>>2)-1];
   reg [31:0]  regname [0:31];

   integer    i, reg_old, reg_new;

   always begin clk = 0; #10; clk = 1; #10; end

   task initmem;
      input [31:0] addr;
      input [31:0] text;
      input [319:0] source;

      begin
         if( addr & 3 ) begin
            $display("Unaligned instruction address, 0x%h",addr);
            $stop;
         end

         if( addr < `MEMBASE || addr >= `MEMBASE + `TEXTSIZE ) begin
            $display("Address 0x%h out of range.",addr);
            $stop;
         end

         {dut.mem[addr],dut.mem[addr+1],dut.mem[addr+2],dut.mem[addr+3]} = text;

         while( source[319:312] === 8'b0 ) source = {source[311:0]," "};
         isource[addr>>2] = source;

      end

   endtask

   task initdmem;
      input [31:0] addr;
      input [31:0] word;

      reg [31:0] daddr;

      begin
         if( addr & 3 ) begin
            $display("Unaligned data address, 0x%h",addr);
            $stop;
         end

         if( addr < `DATABASE || `A(addr) >= `MEMBASE + `MEMSIZE ) begin
            $display("Data address 0x%h out of range.",addr);
            $stop;
         end

         daddr = `A(addr);

         {dut.mem[daddr],dut.mem[daddr+1],
          dut.mem[daddr+2],dut.mem[daddr+3]} = word;

      end

   endtask

   function [4:0] ito5;
      input i;
      integer i;
      ito5 = i;
   endfunction


   integer rno;

   task initregs;
      input [31:0] name;
      input [3:0] cnt;
      integer i;

      for(i=0; i<cnt; i=i+1) begin
         regname[rno] = name + i;
         rno = rno + 1;
      end

   endtask

   initial begin

      cycle_count = 0;

      begin

         regname[0] = "zero";
         regname[1] = "at";
         rno = 2;
         initregs("v0",2);
         initregs("a0",4);
         initregs("t0",8);
         initregs("s0",8);
         initregs("t8",2);
         initregs("k0",2);
         regname[28] = "gp";
         regname[29] = "sp";
         regname[30] = "fp";
         regname[31] = "ra";

      end

      `include `MIPS_PROG

      for(i=0; i<32; i=i+1) dut.gpr[i] = i*10;

      for(i=0; i<32; i=i+1) gpr_shadow[i] = dut.gpr[i];

      dut.pc = 'h400000;
      dut.npc = dut.pc + 4;

      wait( clk == 0 );

      forever begin
         $display("PC 0x%h:  %-s",dut.pc,isource[dut.pc>>2]);
         @( negedge clk );
         for(i=0; i<32; i=i+1)
           if( gpr_shadow[i] !== dut.gpr[i] ) begin
              reg_old = gpr_shadow[i];
              reg_new = dut.gpr[i];
              $display(" Register $%d (%s): 0x%h (%d) -> 0x%h (%d)",
                       ito5(i), regname[i], gpr_shadow[i],
                       reg_old, dut.gpr[i], reg_new);
              gpr_shadow[i] = dut.gpr[i];
           end

         if( exception ) begin
            $display("Exception, stopping simulation.");
            $stop;
         end

         cycle_count = cycle_count + 1;
         if( cycle_count >= cycle_limit ) begin
            $display("Cycle count limit reached, stopping.");
            $stop;
         end
      end

   end

endmodule