/// LSU EE 3755 -- Spring 2002 -- Computer Organization
//
/// Example used in class 1 May 2002
//
/// Hardwired Control, Multicycle MIPS, Day 2
// 
//
//  Will be added to each day.
//
 /// Day 1:  29 April 2002
//
// Started moving memory out of the processor by adding ports for an
// external memory.  The module system (at the end of this file)
// connects cpu to the memory.  Not in a working state.
//
// Copied functional simulator code.
// Added system module that instantiates cpu and memory.
// Added new ports.
//
 /// Day 2:  1 May 2002
//
// Removed old mem code.
// Added states for instruction fetch, decode, and execute.
// Was working during class, but by the end of class was in a
// non-working state.

`define MIPS_PROG "tfirst.v"

module cpu(exc,data_out,addr,size,we,data_in,mem_error_in,reset,clk);
   input [31:0] data_in;
   input [2:0]  mem_error_in;
   input        reset,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] 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 [31:0] gpr [0:31];

   reg [31:0] simmed, uimmed;
   reg [7:0]  temp;
   
   reg [2:0]  state;
   
   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;

   parameter  ST_if = 1;  // Instruction Fetch
   parameter  ST_id = 2;  // Instruction Decode
   parameter  ST_ex = 3;  // Executing

   alu our_alu(alu_out, alu_a, alu_b, alu_op);

   always @( state or pc )
     case( state )
       ST_if:
         begin
            addr = pc;
            size = 3;
            we = 0;
         end
       default:
         begin
            size = 0;
         end

     endcase
   
   always @( posedge clk )
     if( reset ) begin

        state = ST_if;
        pc = 'h400000;
        npc = pc + 4;
        exc = 0;

     end else
   case( state )

     /// IF: Instruction Fetch
     ST_if:
       begin

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

          //  Provide address to the memory, and other info.

          ir = data_in;
          state = ST_id; // Next state

       end

     /// Instruction Decode
     ST_id:
       begin

          //  Read data from memory.


          {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: begin
                   alu_op = OP_add;
                   alu_a = gpr[rs];
                   alu_b = gpr[rt];
                   dst = rd;
                end
                F_sub: gpr[rd] = gpr[rs] - gpr[rt];
                F_sll: gpr[rd] = gpr[rt] << sa;
                F_jr: nnpc = gpr[rs];
                default: exc = 1;
              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: exc = 1;

          endcase

          pc = npc;
          npc = nnpc;
          
          state = ST_ex;
       end

     /// EX: Execute
     ST_ex:
       begin
          if( dst ) gpr[dst] = alu_out;
       end
     
   endcase

endmodule

module alu(alu_out, alu_a, alu_b, alu_op);
   output [31:0] alu_out;
   input [31:0]  alu_a, alu_b;
   input [5:0]   alu_op;

   reg [31:0]    alu_out;

   // Control Signal Value Names
   parameter  OP_nop = 0;
   parameter  OP_sll = 1;
   parameter  OP_srl = 2;
   parameter  OP_add = 3;
   parameter  OP_sub = 4;
   parameter  OP_or  = 5;
   parameter  OP_and = 6;
   parameter  OP_slt = 7;
   parameter  OP_seq = 8;
   
   always @( alu_a or alu_b or alu_op )
     case( alu_op )
       OP_add  : alu_out = alu_a + alu_b;
       OP_and  : alu_out = alu_a & alu_b;
       OP_or   : alu_out = alu_a | alu_b;
       OP_sub  : alu_out = alu_a - alu_b;
       OP_slt  : alu_out = {alu_a[31],alu_a} < {alu_b[31],alu_b};
       OP_sll  : alu_out = alu_b << alu_a;
       OP_srl  : alu_out = alu_b >> alu_a;
       OP_seq  : alu_out = alu_a == alu_b;
       OP_nop  : alu_out = 0;
       default : begin alu_out = 0;  $stop;  end
     endcase
   
endmodule
   

module system(exc,reset,clk);
   input reset, 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,reset,clk);
   mips_memory_1p m1(mem_data_out,mem_err_out,addr,size,we,cpu_data_out,clk);
   
endmodule

`include "mips_hc_tb.v"