/// LSU EE 3755 -- Fall 2001 -- Computer Organization
//
/// Unsyn. Separate Memory MIPS, As Seen in Class


`define MIPS_PROG "ls.v"


module system(exc,clk);
   input clk;
   output [7:0] exc;

   wire [31:0] cpu_data_out, addr, mem_data_out;
   wire [2:0]  mem_err_out;
   wire [1:0]  size;
   wire        we;

   cpu cpu1(exc,cpu_data_out,addr,size,we,mem_data_out,mem_err_out,clk);
   memory_2 m1(mem_data_out,mem_err_out,addr,size,we,cpu_data_out);

endmodule

module cpu(exc,data_out,addr,size,we,data_in,mem_error_in,clk);
   input [31:0] data_in;
   input [2:0]  mem_error_in;
   input clk;
   output [7:0] exc;
   output [31:0] data_out, addr;
   output [1:0]  size;
   output        we;

   reg [31:0]    data_out, addr;
   reg [1:0]     size;
   reg           we;
   reg [7:0]     exc;

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

   reg [31:0] pc, npc, nnpc;
   reg [31:0] ir;

   reg [4:0]  rs, rt, rd, sa;
   reg [5:0]  opcode, func;
   integer    i;

   // Values for funct field.
   parameter  f_sll = 6'h0;
   parameter  f_srl = 6'h2;
   parameter  f_add = 6'h20;
   parameter  f_sub = 6'h22;
   parameter  f_or  = 6'h25;

   // Values for opcode field.
   parameter  o_rfmt = 6'h0;
   parameter  o_j    = 6'h2;
   parameter  o_beq  = 6'h4;
   parameter  o_bne  = 6'h5;
   parameter  o_addi = 6'h8;
   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_sw   = 6'h2b;
   parameter  o_sb   = 6'h28;

   initial begin exc = 0; i = 0; end

   reg [15:0] immed, highhalfofsignedimmed;
   reg [31:0] simmed, uimmed;
   reg [25:0] ii;

   always @( posedge clk ) begin

      addr = pc;
      size = 3;
      we = 0;
      #1;
      ir = data_in;

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

      immed = ir[15:0];
      highhalfofsignedimmed = immed[15] ? 16'hffff : 16'h0;
      simmed = { highhalfofsignedimmed, immed };
      uimmed = { 16'h0, immed };
      ii = ir[25:0];

      nnpc = npc + 4;

      case( opcode )

        o_rfmt:

          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;
            default: exc = 1;

          endcase

        o_lbu:
          begin
             addr = gpr[rs] + simmed;
             we = 0;
             size = 1;
             #1;
             gpr[rt] = data_in;
          end
        o_sb: begin:A
           reg [31:0] rt_val;
           data_out = gpr[rt];
           addr = gpr[rs] + simmed;
           we = 1;
           size = 1;
        end
        
        o_lui: gpr[rt] = { immed, 16'b0 };
        o_addi: gpr[rt] = gpr[rs] + simmed;
        o_andi: gpr[rt] = gpr[rs] & uimmed;
        o_bne:  if( gpr[rt] != gpr[rs] ) nnpc = npc + ( simmed << 2 ) ;
        o_beq:  if( gpr[rt] == gpr[rs] ) nnpc = npc + ( simmed << 2 ) ;
        o_j: nnpc = { pc[31:28], ii, 2'b0 };

        default: exc = 1;

      endcase

      gpr[0] = 0;

      pc = npc;
      npc = nnpc;

   end

endmodule



`include "mipsi2tb.v"