/// LSU EE 3755 -- Fall 2001 -- Computer Organization
//
/// Hardwired Control MIPS, As Seen in Class [tm]
//
// Time-stamp: <3 December 2001, 13:32:51 CST, koppel@sol>
//
// This file contains the Verilog code for the hardwired control MIPS
// implementation being written in class.  It is probably not complete
// and might not be working, even considering its incomplete form.
// Completed code will also be posted.
//
// $Date: 2001/12/03 20:59:19 $
//
// $Log: mips3l.v,v $
// Revision 1.4  2001/12/03 20:59:19  koppel
// Completed combinational logic for setting memory inputs.
//
// Revision 1.3  2001/11/30 21:06:13  koppel
// Moved branch condition and target computation to ALU.
// Started on combinational logic code for setting memory inputs,
// this code currently incomplete and so it is ifdef'ed out.
//
// Revision 1.2  2001/11/30 16:14:02  koppel
// Completed separation of ALU, used for arithmetic & logic and for
// effective address computation.  Not yet used for branches.
//
// Revision 1.1  2001/11/28 18:10:49  koppel
// Initial revision
//
//
//  Hi-lock: (("st_[a-z_]+:" (0 (quote font-lock-note-face) t))) 


`define MIPS_PROG "prog1.v"


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);
   memory_3 m1(mem_data_out,mem_err_out,addr,size,we,cpu_data_out,clk);

endmodule

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] 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;

   // 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;

   initial begin exc = 0; i = 0; end

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

   reg [2:0]  state;

   wire [31:0] alu_out;
   reg [31:0]  alu_a, alu_b;
   reg [5:0]   alu_op;
   wire        alu_zero;

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

   parameter  st_if = 1;
   parameter  st_id = 2;
   parameter  st_ex = 3;
   parameter  st_ex_addr = 5;
   parameter  st_ex_cond = 6;
   parameter  st_ex_targ = 7;
   parameter  st_me = 4;

   reg [31:0] rs_val, rt_val;
   reg [4:0]  wb_rd;
   reg        me_we;
   reg [1:0]  me_size;
   reg        ex_cond_take;

   always @( state or pc or alu_out or me_we or me_size )
     case( state )
       st_if:   begin addr = pc; we = 0; size = 3; end
       st_me:   begin addr = alu_out; we = me_we; size = me_size; end
       default: begin addr = alu_out; we =0; size = 0; end
     endcase
   

   always @( posedge clk )
     if( reset ) begin

        pc = 'h400000;
        npc = pc + 4;

        state = st_if;

     end else
     case ( state )

       st_if:
         begin
            ir = data_in;
            state = st_id;
         end

       st_id:
         begin

            {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;

            rs_val = gpr[rs];
            rt_val = gpr[rt];

            case( opcode )

              o_rfmt:

                case ( func )

                  f_add:
                    begin
                       alu_a = rs_val;
                       alu_b = rt_val;
                       alu_op = op_add;
                       wb_rd = rd;
                    end
                  
                  f_sub:
                    begin
                       alu_a = rs_val;
                       alu_b = rt_val;
                       alu_op = op_sub;
                       wb_rd = rd;
                    end
                  f_sll:
                    begin
                       alu_a = rt_val;
                       alu_b = sa;
                       alu_op = op_sll;
                       wb_rd = rd;
                    end


                  default: exc = 1;

                endcase

              o_lbu:
                begin
                   alu_a = rs_val;
                   alu_b = simmed;
                   alu_op = op_add;
                   wb_rd = rt;
                   me_we = 0;
                   me_size = 1;
                   //  addr = gpr[rs] + simmed; we = 0; size = 1;
                end
              o_sb: begin
                 alu_a = rs_val;
                 alu_b = simmed;
                 alu_op = op_add;
                 wb_rd = 0;
                 me_we = 1;
                 me_size = 1;
              end

              o_lui:
                begin
                   alu_a = uimmed;
                   alu_b = 16;
                   alu_op = op_sll;
                   wb_rd = rt;
                end

              o_addi:
                begin
                   alu_a = rs_val;
                   alu_b = simmed;
                   alu_op = op_add;
                   wb_rd = rt;
                end
              
              o_andi:
                begin
                   alu_a = rs_val;
                   alu_b = uimmed;
                   alu_op = op_and;
                   wb_rd = rt;
                end

              o_bne:
                begin
                   alu_a = rs_val;
                   alu_b = rt_val;
                   alu_op = op_sub;
                   wb_rd = 0;
                   ex_cond_take = 0;
                end
              
              o_beq:
                begin
                   alu_a = rs_val;
                   alu_b = rt_val;
                   alu_op = op_sub;
                   wb_rd = 0;
                   ex_cond_take = 1;
                end
              
              o_j: nnpc = { pc[31:28], ii, 2'b0 };

              default: exc = 1;

             endcase

            data_out = rt_val;

            pc = npc;
            npc = nnpc;

            case( opcode )
              o_bne, o_beq: state = st_ex_cond;
              o_lbu, o_sb: state = st_ex_addr;
              default: state = st_ex;
            endcase
         end

       st_ex:
         begin

            if( wb_rd ) gpr[ wb_rd ] = alu_out;
            state = st_if;
            
         end

       st_ex_addr:
         begin
            state = st_me;
         end

       st_ex_cond:
         begin
            if( ex_cond_take == alu_zero ) begin
               // Taken
               alu_a = pc;
               alu_b = simmed << 2;
               alu_op = op_add;
               wb_rd = 0;
               state = st_ex_targ;
            end else begin
               // Not Taken
               state = st_if;
            end
         end

       st_ex_targ:
         begin
            npc = alu_out;
            state = st_if;
         end

       st_me:
         begin
            if( !we ) gpr[wb_rd] = data_in;
            state = st_if;
         end

       default:
         begin
            $display("Unexpected state.");
            $stop;
         end
       
     endcase

endmodule

module alu(zero,alu_out,alu_a,alu_b,alu_op);
   output [31:0] alu_out;
   output        zero;
   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;

   assign     zero = alu_out == 0;
   
   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 < alu_b;
       op_sll  : alu_out = alu_a << alu_b;
       op_srl  : alu_out = alu_a >> alu_b;
       op_nop  : alu_out = 0;
       default : begin alu_out = 0;  $stop;  end
     endcase
       
   
endmodule


`include "mipsi3tb.v"