////////////////////////////////////////////////////////////////////////////////
///
/// LSU EE 3755 -- Fall 2012
///
/// Homework 6

 /// Name:  

 /// Instructions:
  //
  // Copy this to a file named hw06.v to directory ~/hw06 in your
  // class account. (~ is your home directory.)  Use this
  // file for your solution.  Your entire solution should be in
  // this file.
  //
  // Do not rename the modules in this file and be sure to use the
  // directory and filename given above.
  //
  // Modify the same cpu module for Problems 1 and 2.

  // Assignment: http://www.ece.lsu.edu/ee3755/2012f/hw06.pdf

////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
/// Problems 1 and 2

 /// General Instructions
//
// For clickable links visit an html version of this file:
// http://www.ece.lsu.edu/ee3755/2012f/hw06.v.html
//
// Put the solutions to Problems 1 and 2 in the cpu module below.
// Don't duplicate or rename it.
//
// The testbench for both problems is "test_proc".
//
//   It should issue PASS and FAIL messages for each problem,
//   and finish by displaying "End of testbench run."
//
//   By default it runs test program: 
//   Web:   http://www.ece.lsu.edu/ee3755/2012f/hw06-bm.s.html
//   Local: /home/faculty/koppel/pub/ee3755/s/2012_Fall/hw06/hw06-bm.s
//
//   To use a different test program put its name in the macro below
//   (end it with a .v even though the assembler code is in a file
//   with a .s extension) and "assemble" the program by pressing
//   [S-f9] while in an Emacs buffer containing the program.
//
`define MIPS_PROG "/home/faculty/koppel/pub/ee3755/s/2012_Fall/hw06/hw06-bm.v"
//
//   The testbench will not pass Problem 1 unless the test program
//   above is used.
//
// Additional states (st_if, st_id, etc.) can be added but st_id must
// be used for decode and its value must remain 2.
//
// Solutions must be reasonably efficient, fast, and synthesizable.
//
//   Do not use delays or do anything on the negative edge of the clock.
//


// See:  http://www.ece.lsu.edu/ee4720/mips32v2.pdf  for instruction info.




 /// Modify The Module Below
//


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;

   // MIPS Registers
   //
   reg [31:0] gpr [0:31];
   reg [31:0] pc, npc;
   reg [31:0] ir;

   // Instruction Fields
   //
   reg [4:0]  rs, rt, rd, sa;
   reg [5:0]  opcode, func;
   reg [25:0] ii;
   reg [15:0] immed;

   // Values Derived From Immediates and Read From Register File
   //
   reg [31:0] simmed, uimmed, limmed;
   reg [31:0] rs_val, rt_val, sa_val;

   reg [75:0]     bndl;

   // ALU Connections
   //
   wire [31:0] alu_out;
   reg [31:0]  alu_a, alu_b;
   reg [5:0]   alu_op;

   // Processor Control Logic State
   //
   reg [2:0] state;

   reg [4:0] wb_rd;     // Register number to write.
   reg       me_we;     // we value to use in state st_me
   reg [1:0] me_size;   // size value to use in state st_me

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

   // Values for the MIPS 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 the MIPS 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;

   // Processor Control Logic States
   //
   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;

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

   // Handy Constant
   //
   parameter  R0 = 5'd0;


   /// Set Memory Connection Values: addr, we, and size.
   //
   always @( state or pc or alu_out or me_size or me_we )
     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 = pc;       we = 0;      size = 0;       end
       // Note: addr is set for default case to simplify synthesized hardware.
     endcase

   always @( posedge clk )
     if ( reset ) begin

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

     end else
     case ( state )

       ///       Instruction Fetch
       ST_if:
         begin
            ir    = data_in;
            state = ST_id;
         end

       ///       Instruction Decode (and Register Read)
       ST_id:
         begin

            {opcode,rs,rt,rd,sa,func} = ir;
            ii     = ir[25:0];
            immed  = ir[15:0];

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

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

            // Set alu_a, alu_b, alu_op, and wb_rd.
            //
            case ( opcode )

              O_rfmt:
                // R-Format Instructions
                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};
                  default:
                    begin bndl = {rd, sa_val, OP_sll, rt_val}; exc = 1; end
                endcase

              // I- and J-Format Instructions
              O_lbu:  bndl = {rt, rs_val, OP_add, simmed };
              O_sb:   bndl = {R0, rs_val, OP_add, simmed };
              O_lui:  bndl = {rt, rs_val, OP_or,  limmed };
              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_slti: bndl = {rt, rs_val, OP_slt, simmed };
              O_j:    bndl = {R0, rs_val, OP_nop, simmed };
              O_bne, O_beq: bndl = {R0, rs_val, OP_seq, rt_val };
              default: begin bndl = {R0, rs_val, OP_seq, rt_val }; exc = 1; end
            endcase

            { wb_rd, alu_a, alu_op, alu_b } = bndl;

            // Needed for a store instruction, doesn't hurt others.
            data_out = rt_val;

            // Set me_size and me_wb
            //
            case ( opcode )
              O_lbu   : begin me_size = 1;  me_we = 0; end
              O_sb    : begin me_size = 1;  me_we = 1; end
              default : begin me_size = 0;  me_we = 0; end
            endcase

            pc = npc;

            // Set npc, branch instruction may change npc.
            //
            case ( opcode )
              O_j     : npc = { pc[31:28], ii, 2'b0 };
              default : npc = pc + 4;
            endcase

            case ( opcode )
              O_lbu, O_sb  : state = ST_ex_addr;
              O_bne, O_beq : state = ST_ex_cond;
              O_j          : state = ST_if;
              default      : state = ST_ex;
            endcase
         end

       ///       Execute -- ALU instructions
       ST_ex:
         begin
            if ( wb_rd ) gpr[wb_rd] = alu_out;
            state = ST_if;
         end

       ///       Execute -- Compute Effective Address for Loads and Stores
       ST_ex_addr:
         begin
            state = ST_me;
         end

       ///       Execute -- Compute Branch Condition
       ST_ex_cond:
         begin
            if ( opcode == O_beq && alu_out
                 || opcode == O_bne && !alu_out ) begin
               alu_a  = pc;
               alu_b  = simmed << 2;
               alu_op = OP_add;
               state  = ST_ex_targ;
            end else begin
               state = ST_if;
            end
         end

       ///       Execute -- Compute Branch Target
       ST_ex_targ:
         begin
            npc = alu_out;
            state = ST_if;
         end

       ///       Memory
       ST_me:
         begin
            if ( wb_rd ) gpr[wb_rd] = data_in;
            state = ST_if;
         end

       default:
         begin
            $display("Unexpected state.");
            $stop;
         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

// cadence translate_off

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

// cadence translate_on

`include "/home/faculty/koppel/pub/ee3755/s/2012_Fall/hw06/mips_hc_tb.v"