`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 28.02.2019 12:05:37
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module top(
    input clock,
    input reset,
    inout [15:0] gpio_A,
    inout [15:0] gpio_B
);
    parameter ELEM_SIZE = 8;
    parameter DATA_SIZE = 32;
    parameter INSTR_SIZE = 32;
    parameter IMEM_SIZE = 1024;
    parameter DMEM_SIZE = 1024;
    parameter ADDR_IMEM_SIZE = $clog2(IMEM_SIZE);
    parameter ADDR_DMEM_SIZE = $clog2(DMEM_SIZE);
    parameter REG_NUM = 32;
    parameter ADDR_REG_SIZE = $clog2(REG_NUM);
    
    
    wire [ADDR_IMEM_SIZE-1:0] pc_next;
    wire [ADDR_IMEM_SIZE-1:0] pc;
    wire [ADDR_IMEM_SIZE-1:0] pc_incr;
    wire [ADDR_IMEM_SIZE-1:0] pc_branch_word;
    wire [ADDR_IMEM_SIZE-1:0] pc_branch;
    
    wire [INSTR_SIZE-1:0] instruction;
    wire [31:0] immediate;
    
    wire [ADDR_REG_SIZE-1:0] reg_addr_rd_2;
    wire [DATA_SIZE-1:0] alu_op_a;
    wire [DATA_SIZE-1:0] reg_rd_2;
    
    wire [DATA_SIZE-1:0] instr_ext;
    wire [DATA_SIZE-1:0] alu_op_b;
    wire [DATA_SIZE-1:0] alu_result;
    wire [3:0] alu_opcode;
    
    wire [DATA_SIZE-1:0] data_bus;
    wire [ADDR_DMEM_SIZE-1:0] addr_dmem;
    wire en_dmem;
    wire en_IDR_A;
    wire en_ODR_A;
    wire en_MODER_A;
    wire en_IDR_B;
    wire en_ODR_B;
    wire en_MODER_B;
    
    wire [DATA_SIZE-1:0] data_out_mem;  
    wire [DATA_SIZE-1:0] reg_write;
    
    wire Reg2Loc;
    wire ALUSrc;
    wire MemtoReg;
    wire RegWrite;
    wire MemRead;
    wire MemWrite;
    wire Branch;
    wire Uncondbranch;
    wire [1:0] ALUOp;
    wire Zero;

    // program counter register and logic
    /////////////////////////////////////////////////////    
    register #(
        .DATA_SIZE(ADDR_IMEM_SIZE)
    ) register_pc (
        .clock(clock),
        .reset(reset),
        .load(1'b1),
        .data_in(pc_next),
        .data_out(pc)
    );
    
    adder #(
        .DATA_SIZE(ADDR_IMEM_SIZE)
    ) adder_pc (
        .op_a(pc),
        .op_b(32'd4),
        .sum(pc_incr)      
    );
    
    shifter #(
        .DATA_SIZE(DATA_SIZE),
        .LEFT_N_RIGHT(1),
        .BIT_NUM(2)
    ) shifter_branch (
        .data_in(immediate),
        .data_out(pc_branch_word)
    );
    
    adder #(
        .DATA_SIZE(ADDR_IMEM_SIZE)
    ) adder_branch (
        .op_a(pc_incr),
        .op_b(pc_branch_word),
        .sum(pc_branch)      
    );
    
    multiplexer #(
        .DATA_SIZE(ADDR_IMEM_SIZE)
    ) mux_PCSrc (
        .selector( (Branch & Zero) | Uncondbranch ),
        .data_sel_0(pc_incr),
        .data_sel_1(pc_branch),
        .data_out(pc_next)
        
    );
    /////////////////////////////////////////////////////    

    // instruction memory
    /////////////////////////////////////////////////////    
    memory #(
        .WORD_SIZE(INSTR_SIZE),
        .ELEM_SIZE(ELEM_SIZE),
        .MEM_SIZE(IMEM_SIZE),
        .INIT_FILE("imem.mem")
    ) imem (
        .clock(clock),
        .wr_en(1'b0),
        .rd_en(1'b1),
        .address(pc),
        .data_in({INSTR_SIZE{1'b0}}),
        .data_out(instruction)
    );
        
    sign_extend sign_ext (
        .instruction(instruction),
        .data_out(immediate)
    );
    /////////////////////////////////////////////////////  

    // register file
    /////////////////////////////////////////////////////      
    multiplexer #(
        .DATA_SIZE(DATA_SIZE)
    ) mux_Reg2Loc (
        .selector(Reg2Loc),
        .data_sel_0(instruction[20:16]),
        .data_sel_1(instruction[4:0]),
        .data_out(reg_addr_rd_2)
    );
    
    register_file #(
        .DATA_SIZE(DATA_SIZE),
        .REG_NUM(REG_NUM)
    ) regfile (
        .clock(clock),
        .reset(reset),
        .wr_en(RegWrite),
        .addr_wr(instruction[4:0]),
        .addr_rd_1(instruction[9:5]),
        .addr_rd_2(reg_addr_rd_2),
        .data_wr(reg_write),
        .data_rd_1(alu_op_a),
        .data_rd_2(reg_rd_2)
    );
    /////////////////////////////////////////////////////  

    // ALU
    /////////////////////////////////////////////////////        
    multiplexer #(
        .DATA_SIZE(DATA_SIZE)
    ) mux_ALUsrc (
        .selector(ALUSrc),
        .data_sel_0(reg_rd_2),
        .data_sel_1(immediate),
        .data_out(alu_op_b)
    );
    
    alu_control alu_ctrl (
        .alu_op(ALUOp),
        .instr_opcode(instruction[31:21]),
        .alu_opcode(alu_opcode)
    );
  
    alu #(
        .DATA_SIZE(DATA_SIZE)
    ) alu_unit (
        .opcode(alu_opcode),
        .op_a(alu_op_a),
        .op_b(alu_op_b),
		.shamt(instruction[15:10]),
        .zero(Zero),
        .result(alu_result)
    );
    /////////////////////////////////////////////////////
    
    // data memory
    /////////////////////////////////////////////////////  
    
	// decoder dmem
    decoder_dmem #(
        .BASE_ADDR(32'h20000000),
        .MEM_SIZE(DMEM_SIZE)
    ) dec_dmem (
        .address(alu_result),
        .en_dmem(en_dmem),
        .addr_dmem(addr_dmem)
    );
	
	// memory
    memory #(
        .WORD_SIZE(DATA_SIZE),
        .ELEM_SIZE(ELEM_SIZE),
        .MEM_SIZE(DMEM_SIZE),
        .INIT_FILE("dmem.mem")
    ) dmem (
        .clock(clock),
        .wr_en(MemWrite && en_dmem),
        .rd_en(MemRead && en_dmem),
        .address(addr_dmem),
        .data_in(data_bus),
        .data_out(data_out_mem)
    );
	
    assign data_bus = (MemRead && en_dmem) ? data_out_mem : 32'bz;
	assign data_bus = (MemWrite) ? reg_rd_2 : 32'bz;
    
	// write reg multiplexer
    multiplexer #(
        .DATA_SIZE(DATA_SIZE)
    ) mux_MemtoReg (
        .selector(MemtoReg),
        .data_sel_0(alu_result),
        .data_sel_1(data_bus),
        .data_out(reg_write)
    );
    /////////////////////////////////////////////////////  

	// port A
    /////////////////////////////////////////////////////
	
    // port A decoder
    decoder_port #(
            .BASE_ADDR(32'h48000000)
    ) dec_port_A (
        .address(alu_result),
        .en_IDR(en_IDR_A),
        .en_ODR(en_ODR_A),
        .en_MODER(en_MODER_A)
    );
	
	// port A logic
    port port_A(
        .clock(clock),
        .reset(reset),
        .wr_en(MemWrite),
        .rd_en(MemRead),
        .en_IDR(en_IDR_A),
        .en_ODR(en_ODR_A),
        .en_MODER(en_MODER_A),
        .pin(gpio_A),
        .data_bus(data_bus)
    );
    /////////////////////////////////////////////////////

	// port B
    /////////////////////////////////////////////////////
	
    // port B decoder
    decoder_port #(
            .BASE_ADDR(32'h48000400)
    ) dec_port_B (
        .address(alu_result),
        .en_IDR(en_IDR_B),
        .en_ODR(en_ODR_B),
        .en_MODER(en_MODER_B)
    );
	
	// port B logic
    port port_B(
        .clock(clock),
        .reset(reset),
        .wr_en(MemWrite),
        .rd_en(MemRead),
        .en_IDR(en_IDR_B),
        .en_ODR(en_ODR_B),
        .en_MODER(en_MODER_B),
        .pin(gpio_B),
        .data_bus(data_bus)
    );
    /////////////////////////////////////////////////////	
    // control
    /////////////////////////////////////////////////////  
    control ctrl(
        .instr_opcode(instruction[31:21]),
        .Reg2Loc(Reg2Loc),
        .ALUSrc(ALUSrc),
        .MemtoReg(MemtoReg),
        .RegWrite(RegWrite),
        .MemRead(MemRead),
        .MemWrite(MemWrite),
        .Branch(Branch),
		.Uncondbranch(Uncondbranch),
        .ALUOp(ALUOp)
    );
    /////////////////////////////////////////////////////        

endmodule
