/*
 *
 * Redistributions of any form whatsoever must retain and/or include the
 * following acknowledgment, notices and disclaimer:
 *
 * This product includes software developed by Carnegie Mellon University. 
 *
 * Copyright (c) 2004 by Babak Falsafi and James Hoe,
 * Computer Architecture Lab at Carnegie Mellon (CALCM), 
 * Carnegie Mellon University.
 *
 * This source file was written and maintained by Jared Smolens 
 * as part of the Two-Way In-Order Superscalar project for Carnegie Mellon's 
 * Introduction to Computer Architecture course, 18-447. The source file
 * is in part derived from code originally written by Herman Schmit and 
 * Diana Marculescu.
 *
 * You may not use the name "Carnegie Mellon University" or derivations 
 * thereof to endorse or promote products derived from this software.
 *
 * If you modify the software you must place a notice on or within any 
 * modified version provided or made available to any third party stating 
 * that you have modified the software.  The notice shall include at least 
 * your name, address, phone number, email address and the date and purpose 
 * of the modification.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY WARRANTY OF ANY KIND, EITHER 
 * EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO ANYWARRANTY 
 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS OR BE ERROR-FREE AND ANY 
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, 
 * TITLE, OR NON-INFRINGEMENT.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY 
 * BE LIABLE FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO DIRECT, INDIRECT, 
 * SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN 
 * ANY WAY CONNECTED WITH THIS SOFTWARE (WHETHER OR NOT BASED UPON WARRANTY, 
 * CONTRACT, TORT OR OTHERWISE).
 *
 */

// Include the MIPS constants
`include "mips_defines.vh"
`include "mips_internalDefines.vh"

// include MIPS modules
`include "mips_mux2to1.v"
`include "mips_mux4to1.v"
`include "mips_reg.v"
`include "mips_regFile.v"
`include "mips_alu.v"
`include "mips_addConst.v"
`include "mips_decode.v"
`include "mips_exceptionUnit.v"
`include "mips_syscallUnit.v"

////
//// The MIPS standalone processor module
////
////   clk          (input)  - The clock
////   inst_addr    (output) - Address of instruction to load
////   inst         (input)  - Instruction from memory
////   inst_excpt   (input)  - inst_addr not valid
////   mem_addr     (output) - Address of data to load
////   mem_data_in  (output) - Data for memory store
////   mem_data_out (input)  - Data from memory load
////   mem_write_en (output) - Memory write mask
////   mem_excpt    (input)  - mem_addr not valid
////   halted       (output) - Processor halted
////   reset        (input)  - Reset the processor
////   

module mips_core(/*AUTOARG*/
  // Outputs
  inst_addr, mem_addr, mem_data_in, mem_write_en, halted,
  // Inputs
  clk, inst_excpt, mem_excpt, inst, mem_data_out, rst_b
  );
  
  parameter text_start  = 32'h00400000; /* Initial value of $pc */

  // Core Interface
  input         clk, inst_excpt, mem_excpt;
  output [29:0] inst_addr;
  output [29:0] mem_addr;
  input  [31:0] inst, mem_data_out;
  output [31:0] mem_data_in;
  output [3:0]  mem_write_en;
  output        halted;
  input         rst_b;

  // Forced interface signals -- required for synthesis to work OK.
  // This is probably not what you want!
  assign        mem_addr = 0;
  assign        mem_data_in = mem_data_out;
  assign        mem_write_en = 4'b0;

  // Internal signals
  wire [31:0]   pc, nextpc, nextnextpc;
  wire          exception_halt, syscall_halt, internal_halt;
  wire          load_epc, load_bva, load_bva_sel;
  wire [31:0]   rt_data, rs_data, rd_data, alu__out, r_v0;
  wire [31:0]   epc, cause, bad_v_addr;
  wire [4:0]    cause_code;

  // Decode signals
  wire [31:0]   dcd_se_imm, dcd_se_offset, dcd_e_imm, dcd_se_mem_offset;
  wire [5:0]    dcd_op, dcd_funct2;
  wire [4:0]    dcd_rs, dcd_funct1, dcd_rt, dcd_rd, dcd_shamt;
  wire [15:0]   dcd_offset, dcd_imm;
  wire [25:0]   dcd_target;
  wire [19:0]   dcd_code;
  wire          dcd_bczft;
  
  // alu signals
  wire [31:0]   imm_value;
  wire [31:0]   alu_op2;
  
  // PC Management
  mips_reg #(32, text_start) PCReg(pc, nextpc, clk, ~internal_halt, rst_b);
  mips_reg #(32, text_start+4) PCReg2(nextpc, nextnextpc, clk,
                                      ~internal_halt, rst_b);
  mips_addConst #(4) NextPCAdder(nextnextpc, nextpc);
  assign        inst_addr = pc[31:2];

  // Instruction decoding
  assign        dcd_op = inst[31:26];    // Opcode
  assign        dcd_rs = inst[25:21];    // rs field
  assign        dcd_rt = inst[20:16];    // rt field
  assign        dcd_rd = inst[15:11];    // rd field
  assign        dcd_shamt = inst[10:6];  // Shift amount
  assign        dcd_bczft = inst[16];    // bczt or bczf?
  assign        dcd_funct1 = inst[4:0];  // Coprocessor 0 function field
  assign        dcd_funct2 = inst[5:0];  // funct field; secondary opcode
  assign        dcd_offset = inst[15:0]; // offset field
       // Sign-extended offset for branches
  assign        dcd_se_offset = { {14{dcd_offset[15]}}, dcd_offset, 2'b00 };
       // Sign-extended offset for load/store
  assign        dcd_se_mem_offset = { {16{dcd_offset[15]}}, dcd_offset };
  assign        dcd_imm = inst[15:0];        // immediate field
  assign        dcd_e_imm = { 16'h0, dcd_imm };  // zero-extended immediate
       // Sign-extended immediate
  assign        dcd_se_imm = { {16{dcd_imm[15]}}, dcd_imm };
  assign        dcd_target = inst[25:0];     // target field
  assign        dcd_code = inst[25:6];       // Breakpoint code

  // synthesis translate_off
  always @(posedge clk) begin
    // useful for debugging, you will want to comment this out for long programs
    if (rst_b) begin
      $display ( "=== Simulation Cycle %d ===", $time );
      $display ( "[pc=%x, inst=%x] [op=%x, rs=%d, rt=%d, rd=%d, imm=%x, f2=%x] [reset=%d, halted=%d]",
                  pc, inst, dcd_op, dcd_rs, dcd_rt, dcd_rd, dcd_imm, dcd_funct2, ~rst_b, halted);
    end
  end
  // synthesis translate_on

  // Let Verilog-Mode pipe wires through for us.  This is another example
  // of Verilog-Mode's power -- undeclared nets get AUTOWIREd up when we
  // run 'make auto'.
  
  /*AUTOWIRE*/
  // Beginning of automatic wires (for undeclared instantiated-module outputs)
  wire [3:0]		alu__sel;		// From Decoder of mips_decode.v
  wire			ctrl_RI;		// From Decoder of mips_decode.v
  wire			ctrl_Sys;		// From Decoder of mips_decode.v
  wire			ctrl_we;		// From Decoder of mips_decode.v
  // End of automatics

  // Generate control signals
  mips_decode Decoder(/*AUTOINST*/
 	      // Outputs
 	      .ctrl_we		(ctrl_we),
 	      .ctrl_Sys	(ctrl_Sys),
 	      .ctrl_RI		(ctrl_RI),
 	      .alu__sel	(alu__sel[3:0]),
 	      // Inputs
 	      .dcd_op		(dcd_op[5:0]),
 	      .dcd_funct2	(dcd_funct2[5:0]));

  // Register File
  // Instantiate the register file from reg_file.v here.
  // Don't forget to hookup the "halted" signal to trigger the register dump 
  mips_regFile RegFile(
        .clk (clk), 
        .rst_n (rst_b), 
        .rdAddr0 (dcd_rs), 
        .rdAddr1 (dcd_rt), 
        .we (ctrl_we), 
        .wrAddr (dcd_rd), 
        .wrData (alu__out), 
        .halted (internal_halt), 
        .rdData0 (rs_data), 
        .rdData1 (rt_data));

  // synthesis translate_off
  initial begin
    // Delete this block when you are ready to try for real
    $display(""); 
    $display(""); 
    $display(""); 
    $display(""); 
    $display(">>>>> This works much better after you have hooked up the reg file. <<<<<");
    $display(""); 
    $display(""); 
    $display(""); 
    $display(""); 
    $finish;
  end
  // synthesis translate_on

  mux2_1 #(32) se_e_MUX(dcd_se_imm, dcd_e_imm, isSe, imm_value);
  mux2_1 #(32) aluOP2(imm_value, rt_data, isImm, alu_op2);

  // Execute
  mips_alu ALU(.alu__out(alu__out), 
               .alu__op1(rs_data),
               .alu__op2(alu_op2),
               .alu__sel(alu__sel));

  // Miscellaneous stuff (Exceptions, syscalls, and halt)
  mips_exceptionUnit EU(.exception_halt(exception_halt), .pc(pc), .rst_b(rst_b),
                    .clk(clk), .load_ex_regs(load_ex_regs),
                    .load_bva(load_bva), .load_bva_sel(load_bva_sel),
                    .cause(cause_code),
                    .IBE(inst_excpt),
                    .DBE(1'b0),
                    .RI(ctrl_RI),
                    .Ov(1'b0),
                    .BP(1'b0),
                    .AdEL_inst(pc[1:0]?1'b1:1'b0),
                    .AdEL_data(1'b0),
                    .AdES(1'b0),
                    .CpU(1'b0));

  assign r_v0 = 32'h0a; // Good enough for now. To support syscall for real,
                        // you should read the syscall
                        // argument from $v0 of the register file 

  mips_syscallUnit SU(.syscall_halt(syscall_halt), .pc(pc), .clk(clk), .Sys(ctrl_Sys),
                  .r_v0(r_v0), .rst_b(rst_b));
  assign        internal_halt = exception_halt | syscall_halt;
  mips_reg #(1, 0) Halt(halted, internal_halt, clk, 1'b1, rst_b);
  mips_reg #(32, 0) EPCReg(epc, pc, clk, load_ex_regs, rst_b);
  mips_reg #(32, 0) CauseReg(cause,
                             {25'b0, cause_code, 2'b0}, 
                             clk, load_ex_regs, rst_b);
  mips_reg #(32, 0) BadVAddrReg(bad_v_addr, pc, clk, load_bva, rst_b);

endmodule // mips_core
// Local Variables:
// verilog-library-directories:("." "../447rtl")
// End:
