`timescale 1ns / 1ps
module CPU (
    input hardware_clk,
    input hardware_reset,
    output clock_locked,
    output [11:0] bcd_control
);

    // first, we split the clock
    wire clk;
    phase_locked_loop pll (
        .reset(hardware_reset),
        .clk_in1 (hardware_clk),
        .clk_out1(clk),
        .locked(clock_locked)
    );

    wire reset;
    assign reset = hardware_reset || ~clock_locked;

    // Out of IF
    wire [31:0] IF_fetched_instruction;
    wire [31:0] IF_PC_plus_4;

    // Out of ID
    wire [1:0] ID_PC_jump;
    wire [31:0] ID_branch_target;
    wire [31:0] ID_jump_target;
    wire [31:0] ID_jump_register_target;
    wire ID_is_branch;
    wire ID_is_loadword;
    wire ID_register_write;
    wire ID_WB_source;
    wire ID_memory_write;
    wire [4:0] ID_ALU_funtion;
    wire ID_ALU_source1;
    wire ID_ALU_source2;
    wire ID_register_write_desination_source;
    wire [31:0] ID_PC_plus_4;
    wire [31:0] ID_register_file_read_A;
    wire [31:0] ID_register_file_read_B;
    wire [4:0] ID_shamt;
    wire [31:0] ID_extended_immediate;
    wire [4:0] ID_rs_address;
    wire [4:0] ID_rt_address;
    wire [4:0] ID_rd_address;

    // Out of EX
    wire EX_PC_branch;
    wire [31:0] EX_branch_target;
    wire EX_register_write;
    wire EX_WB_source;
    wire EX_memory_write;
    wire [31:0] EX_ALU_result;
    wire [31:0] EX_memory_write_data;
    wire [4:0] EX_register_write_destination;
    wire [4:0] EX_rs_address;
    wire [4:0] EX_rt_address;

    // Out of MEM
    wire [4:0] MEM_rt_address;
    wire MEM_register_write;
    wire MEM_WB_source;
    wire [31:0] MEM_memory_read_data;
    wire [31:0] MEM_ALU_result;
    wire [4:0] MEM_register_write_destination;
    wire MEM_data_memory_write;
    wire [31:0] MEM_data_memory_address;
    wire [31:0] MEM_data_memory_write_data;

    // Out of WB
    wire WB_register_write;
    wire [31:0] WB_register_write_data;
    wire [4:0] WB_register_write_address;

    // Out of hazard control
    wire hazard_IFID_source;
    wire hazard_IDEX_source;
    wire hazard_IF_need_stall;

    // Out of EXforward
    wire [1:0] EXforward_IDA_source;
    wire [1:0] EXforward_IDB_source;

    // Out of data memory
    wire [31:0] datamemory_read_data;
    wire [31:0] bcd_hardwire_control;

    assign bcd_control = bcd_hardwire_control[11:0];

    InstFetch instruction_fetch (
        .clk(clk),
        .reset(reset),
        .branch_target(EX_branch_target),
        .jump_target(ID_jump_target),
        .jump_register_target(ID_jump_register_target),
        .PC_jump(ID_PC_jump),
        .PC_branch(EX_PC_branch),
        .need_stall(hazard_IF_need_stall),
        .fetched_instruction(IF_fetched_instruction),
        .PC_plus_4(IF_PC_plus_4)
    );

    InstDecode instruction_decode (
        .clk(clk),
        .reset(reset),
        .prev_fetched_instruction(IF_fetched_instruction),
        .prev_PC_plus_4(IF_PC_plus_4),
        .IFIDSrc(hazard_IFID_source),
        .WB_write_enable(WB_register_write),
        .WB_write_address(WB_register_write_address),
        .WB_write_data(WB_register_write_data),
        .PC_jump(ID_PC_jump),
        .jump_target(ID_jump_target),
        .jump_register_target(ID_jump_register_target),
        .is_loadword(ID_is_loadword),
        .is_branch(ID_is_branch),
        .WB_source(ID_WB_source),
        .memory_write(ID_memory_write),
        .ALU_function(ID_ALU_funtion),
        .ALU_source1(ID_ALU_source1),
        .ALU_source2(ID_ALU_source2),
        .register_write_destination_source(ID_register_write_desination_source),
        .register_write(ID_register_write),
        .PC_plus_4(ID_PC_plus_4),
        .register_file_read_A(ID_register_file_read_A),
        .register_file_read_B(ID_register_file_read_B),
        .shamt(ID_shamt),
        .extended_immediate(ID_extended_immediate),
        .rs_address(ID_rs_address),
        .rt_address(ID_rt_address),
        .rd_address(ID_rd_address)
    );

    Execution execution (
        .clk(clk),
        .reset(reset),
        .prev_is_branch(ID_is_branch),
        .prev_WB_source(ID_WB_source),
        .prev_memory_write(ID_memory_write),
        .prev_ALU_function(ID_ALU_funtion),
        .prev_ALU_source1(ID_ALU_source1),
        .prev_ALU_source2(ID_ALU_source2),
        .prev_register_write_destination_source(ID_register_write_desination_source),
        .prev_register_write(ID_register_write),
        .prev_PC_plus_4(ID_PC_plus_4),
        .prev_register_file_read_A(ID_register_file_read_A),
        .prev_register_file_read_B(ID_register_file_read_B),
        .prev_shamt(ID_shamt),
        .prev_extended_immediate(ID_extended_immediate),
        .prev_rs_address(ID_rs_address),
        .prev_rt_address(ID_rt_address),
        .prev_rd_address(ID_rd_address),
        .IDEXSrc(hazard_IDEX_source),
        .operandASrc(EXforward_IDA_source),
        .operandBSrc(EXforward_IDB_source),
        .MEM_forwarded_data(MEM_ALU_result),
        .WB_forwarded_data(WB_register_write_data),
        .PC_branch(EX_PC_branch),
        .branch_target(EX_branch_target),
        .register_write(EX_register_write),
        .WB_source(EX_WB_source),
        .memory_write(EX_memory_write),
        .ALU_result(EX_ALU_result),
        .memory_write_data(EX_memory_write_data),
        .register_write_destination(EX_register_write_destination),
        .rs_address(EX_rs_address),
        .rt_address(EX_rt_address)
    );

    MemoryAccess memory_access (
        .clk(clk),
        .reset(reset),
        .prev_register_write(EX_register_write),
        .prev_WB_source(EX_WB_source),
        .prev_memory_write(EX_memory_write),
        .prev_ALU_result(EX_ALU_result),
        .prev_memory_write_data(EX_memory_write_data),
        .prev_register_write_destination(EX_register_write_destination),
        .register_write(MEM_register_write),
        .WB_source(MEM_WB_source),
        .memory_read_data(MEM_memory_read_data),
        .ALU_result(MEM_ALU_result),
        .register_write_destination(MEM_register_write_destination),

        .data_memory_write(MEM_data_memory_write),
        .data_memory_address(MEM_data_memory_address),
        .data_memory_write_data(MEM_data_memory_write_data),
        .data_memory_read_data(datamemory_read_data)
    );

    WriteBack write_back (
        .clk(clk),
        .reset(reset),
        .prev_register_write(MEM_register_write),
        .prev_WB_source(MEM_WB_source),
        .prev_memory_read_data(MEM_memory_read_data),
        .prev_ALU_result(MEM_ALU_result),
        .prev_register_write_destination(MEM_register_write_destination),
        .register_write(WB_register_write),
        .register_write_data(WB_register_write_data),
        .register_write_addr(WB_register_write_address)
    );

    DataMemory #(.START_ADDRESS(32'h40000000), .MEM_SIZE_IN_WORD(512)) data_memory  (
        .clk(clk),
        .reset(reset),
        .address(MEM_data_memory_address),
        .write_enable(MEM_data_memory_write),
        .write_data(MEM_data_memory_write_data),
        .read_data(datamemory_read_data),
        .bcd_hardwire(bcd_hardwire_control)
    );

    ExecutionForward execution_forward (
        .EX_rs_address(EX_rs_address),
        .EX_rt_address(EX_rt_address),
        .MEM_register_write(MEM_register_write),
        .MEM_register_write_address(MEM_register_write_destination),
        .WB_register_write(WB_register_write),
        .WB_register_write_address(WB_register_write_address),
        .IDA_source(EXforward_IDA_source),
        .IDB_source(EXforward_IDB_source)
    );

    HazardUnit hazard_unit (
        .PC_jump(ID_PC_jump),
        .is_loadword(ID_is_loadword),
        .PC_branch(EX_PC_branch),
        .IFID_source(hazard_IFID_source),
        .IDEX_source(hazard_IDEX_source),
        .IF_need_stall(hazard_IF_need_stall)
    );
endmodule
