/// EE 3755 -- Spring 2002 -- Computer Organization
//
/// Example used in class 6 May 2002
//
/// Hardwired Control, Multicycle MIPS, Day 4
// 
//
//  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.
//
 /// Day 3: 3 May 2002
//
// Got states working.
// Re-wrote ID to take advantage of separate ALU for arithmetic and log. inst.
// Re-wrote ID to take advantage of separate ALU for branches.
//
 /// Day 4: 6 May 2002
//
// Added load instruction.
// Started store instruction.



`define MIPS_PROG "tthird.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;
   reg [5:0]  dst;

   reg [74:0] bndl;
   reg [31:0] limmed, rs_val, rt_val, sa_val, bimmed;
   
   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;

   // Control Signal Value Names
   parameter  OP_nop = 6'd0;
   parameter  OP_sll = 6'd1;
   parameter  OP_srl = 6'd2;
   parameter  OP_add = 6'd3;
   parameter  OP_sub = 6'd4;
   parameter  OP_or  = 6'd5;
   parameter  OP_and = 6'd6;
   parameter  OP_slt = 6'd7;
   parameter  OP_seq = 6'd8;
   parameter  OP_sne = 6'd9;

   parameter  ST_if = 1;  // Instruction Fetch
   parameter  ST_id = 2;  // Instruction Decode
   parameter  ST_ex = 3;  // Executing
   parameter  ST_eb = 4;  // Executing Branch
   parameter  ST_ea = 5;
   parameter  ST_me = 6;

   wire [31:0] alu_out;
   reg [31:0]  alu_a, alu_b;
   reg [5:0]   alu_op;
   
   alu our_alu(alu_out, alu_a, alu_b, alu_op);

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

       default:
         begin
            size = 0;
            addr = pc;
            we = 0;
         end

     endcase

   assign data_out = rt_val;

   
   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 };
          limmed = { immed, 16'h0 };
          bimmed = npc + ( simmed << 2 );
          

          nnpc = npc + 4;  // Reassigned below.

          rs_val = gpr[rs];
          rt_val = gpr[rt];
          sa_val = { 27'd0, sa };

          bndl = 75'bx;

          case( opcode )

            O_tyr:
              case( func )
                F_add: bndl = {rd, rs_val, OP_add, rt_val };
                F_sub: bndl = {rd, rs_val, OP_sub, rt_val };
                F_sll: bndl = {rd, sa_val, OP_sll, rt_val };
                F_jr: nnpc = rs_val;
                default: exc = 1;
              endcase

            O_addi: bndl = {rt, rs_val, OP_add, simmed };
            O_andi: bndl = {rt, rs_val, OP_and, uimmed };
            O_ori: bndl = {rt, rs_val, OP_or, uimmed };
            O_lui: bndl = {rt, rs_val, OP_or, limmed };
            O_bne: bndl = {5'd0, rs_val, OP_sne, rt_val };
            O_beq: bndl = {5'd0, rs_val, OP_seq, rt_val };
            O_j: nnpc = {npc[31:28],ii,2'd0};
            O_jal: begin gpr[31] = nnpc;  nnpc = {npc[31:28],ii,2'd0};  end
            O_lb,O_lbu: bndl = {rt, rs_val, OP_add, simmed };
            O_sb: bndl = {5'd0, rs_val, OP_add, simmed };
            //  O_lbu: gpr[rt] = {24'b0, mem[`A(gpr[rs]+simmed)]};
            //  O_sb: mem[`A(gpr[rs]+simmed)]= gpr[rt];
            default: exc = 1;

          endcase

          if( bndl !== 75'bx ) {dst, alu_a, alu_op, alu_b } = bndl;

          case( opcode )
            O_beq,O_bne: state = ST_eb;
            O_lb,O_lbu: state = ST_ea;
            default: state = bndl === 75'bx ? ST_if : ST_ex;
          endcase

          pc = npc;
          npc = nnpc;
          
       end

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

     /// EXB: Execute Branch
     ST_eb:
       begin
          if( alu_out[0] ) npc = bimmed;
          state = ST_if;
       end

     /// EA: Effective Address
     ST_ea:
       begin
          state = ST_me;
       end

     /// ME: Memory
     ST_me:
       begin
          if( dst != 0 ) gpr[dst] = data_in;
          state = ST_if;
       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 = 6'd0;
   parameter  OP_sll = 6'd1;
   parameter  OP_srl = 6'd2;
   parameter  OP_add = 6'd3;
   parameter  OP_sub = 6'd4;
   parameter  OP_or  = 6'd5;
   parameter  OP_and = 6'd6;
   parameter  OP_slt = 6'd7;
   parameter  OP_seq = 6'd8;
   parameter  OP_sne = 6'd9;

   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_sne  : 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"