/// LSU EE 3755 -- Fall 2012 -- Computer Architecture
//
/// MIPS Processor Functional Simulator
//
// Implements a small subset of MIPS32 instructions and features.
//
// Time-stamp: <31 October 2012, 7:56:15 CDT, koppel@eekopp-1.lsu.edu>
//

/// Dependencies

 /// To compile run this the following is also needed:
//
// mips_fs_tb.v      The testbench.  It is included in here for convenience.
// mipsmemmacros.v   Macros needed for memory access.
//
// An "assembled" program, for example tthird.v.  The name of the
//  file is specified in the MIPS_PROG macro, the testbench uses the
//  macro to include the file.

/// Limitations
//
//  This code will probably never implement all of MIPS32.  The intent
//  is to demonstrate basic MIPS simulation techniques as a first step
//  towards developing synthesizable and implementatble descriptions.
//

 /// Specify Program to Load
//
// A quick-and-dirty method of loading a program is used. (For now.)
// Therefore the description must be re-compiled each time the program
// is changed.  (The program is assembled by SPIM and some perl code.)
//

`define MIPS_PROG "tthird.v"
`include "mipsmemmacros.v"

module proc(exc,clk);
   input clk;
   output exc;

   // add $2, $3, $4

   reg [31:0] pc, npc, nnpc;
   reg [31:0] ir;
   reg [5:0]  opcode, func;
   reg [4:0]  rs, rt, rd, sa;
   reg [15:0] immed;
   reg [25:0] ii;

   reg [7:0]  mem ['h400000:'h400200];

   reg [31:0] gpr [0:31];

   reg [31:0] simmed, uimmed;
   reg [7:0]  temp;
   

   parameter  F_add = 6'h20;
   parameter  F_sll = 6'h0;
   parameter  F_srl = 6'h2;
   parameter  F_sub = 6'h22;
   parameter  F_or  = 6'h25;
   parameter  F_jr  = 6'h8;

   parameter  O_tyr  = 6'h0;
   parameter  O_addi = 6'h8;
   parameter  O_j    = 6'h2;
   parameter  O_jal  = 6'h3;
   parameter  O_beq  = 6'h4;
   parameter  O_bne  = 6'h5;
   parameter  O_slti = 6'ha;
   parameter  O_andi = 6'hc;
   parameter  O_ori  = 6'hd;
   parameter  O_lui  = 6'hf;
   parameter  O_lw   = 6'h23;
   parameter  O_lbu  = 6'h24;
   parameter  O_lb   = 6'h20;
   parameter  O_sw   = 6'h2b;
   parameter  O_sb   = 6'h28;

   
   always @( posedge clk ) begin

      ir = {mem[pc],mem[pc+1],mem[pc+2],mem[pc+3]};

      {opcode, rs, rt, rd, sa, func} = ir;

      immed = ir[15:0];
      ii = ir[25:0];

      simmed = {immed[15] ? 16'hffff : 16'h0, immed};
      uimmed = { 16'h0, immed };

      nnpc = npc + 4;  // Reassigned below.

      case( opcode )

        O_tyr:
         case( func )
           F_add: gpr[rd] = gpr[rs] + gpr[rt];
           F_sub: gpr[rd] = gpr[rs] - gpr[rt];
           F_sll: gpr[rd] = gpr[rt] << sa;
           F_jr:  nnpc = gpr[rs];
           default: $stop;
         endcase

        O_addi: gpr[rt] = gpr[rs] + simmed;
        O_andi: gpr[rt] = gpr[rs] & uimmed;
        O_ori:  gpr[rt] = gpr[rs] | uimmed;
        O_lui:  gpr[rt] = {immed,16'h0};
        O_bne:  if( gpr[rt] != gpr[rs]  ) nnpc = npc + ( simmed << 2 );
        O_j:    nnpc = {npc[31:28],ii,2'd0};
        O_jal:  begin gpr[31] = nnpc;  nnpc = {npc[31:28],ii,2'd0};  end
        O_lbu:  gpr[rt] = {24'b0, mem[`A(gpr[rs]+simmed)]};
        O_lb: 
          begin
             temp = mem[`A(gpr[rs]+simmed)];
             gpr[rt] = {temp[7] ? 24'hffffff : 24'h0, temp};
          end
        O_sb: mem[`A(gpr[rs]+simmed)]= gpr[rt];
        default: $stop;

      endcase

      gpr[0] = 0;

      pc = npc;
      npc = nnpc;

   end

endmodule

`include "mips_fs_tb.v"