`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: nope
// Engineer: Jose
// 
// Create Date: 02/20/2026 09:21:52 AM
// Design Name: RISCV AC Processor Implementation
// Module Name: riscv
// Project Name: riscv-ac
// Target Devices: Artix 7
// Tool Versions: 2025.2
// Description: Top module for interconnection
// 
// Dependencies: 
// 
// Revision: 2.0 - MMIO
// Revision: 1.0 - Basic structure
// Revision: 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module riscv (
    input wire clk,
    input wire rst,
    input wire rx,
    output wire tx,    
    output wire [1:0] leds
    // output wire [7:0] uart_tx_data, // SOLO SIMULACION
    // output wire uart_tx_en // SOLO SIMULACION
);

    // ==========================================
    // BOOTLOADER
    // ==========================================
    wire [31:0] prog_data;
    wire [31:0] prog_addr;
    wire prog_we;
    wire boot_rst;

    uart_bootloader u_boot (
        .clk(clk),
        .rx(rx),
        .prog_data(prog_data),
        .prog_addr(prog_addr),
        .prog_we(prog_we),
        .cpu_rst(boot_rst)
    );

    wire sys_rst = rst | boot_rst;

    // ==========================================
    // ETAPA IF
    // ==========================================
    wire [31:0] npc_IF, pc4_IF, ir_IF, next_pc_IF, pc_stall;
    wire PC_En, IF_ID_En, IF_ID_Clr; 

    assign next_pc_IF = (Branch_Taken) ? branch_target_ID : pc4_IF;
    assign pc4_IF = npc_IF + 4;
    assign pc_stall = (PC_En) ? next_pc_IF : npc_IF;

    pc u_pc (
        .clk(clk), 
        .rst(sys_rst),
        .next_pc(pc_stall),
        .pc_out(npc_IF)
    );

    imem u_imem (
        .clk(clk), 
        // por aqui lee la CPU
        .read_addr(npc_IF), 
        .inst_out(ir_IF),
        // por aqui meto el programa
        .we_ext(prog_we),
        .write_addr_ext(prog_addr),
        .write_data_ext(prog_data)
    );

    // ==========================================
    // REGISTRO IF/ID
    // ==========================================
    wire [31:0] npc_ID, pc4_ID, ir_ID;

    if_id u_if_id (
        .clk(clk), .rst(sys_rst), .en(IF_ID_En), .clr(IF_ID_Clr),
        .pc_in(npc_IF), .pc4_in(pc4_IF), .inst_in(ir_IF),
        .pc_out(npc_ID), .pc4_out(pc4_ID), .inst_out(ir_ID)
    );

    // ==========================================
    // ETAPA ID
    // ==========================================
    wire [4:0] rs1_ID = ir_ID[19:15];
    wire [4:0] rs2_ID = ir_ID[24:20];
    wire [4:0] rd_ID  = ir_ID[11:7];
    wire [31:0] imm_ID, regA_ID, regB_ID;
    
    // Señales de Control
    wire we_reg_ID, we_mem_ID, mem_to_reg_ID, alu_src_ID, branch_ID, jump_ID;
    wire [3:0] alu_op_ID;
    wire Branch_Taken;
    wire [31:0] branch_target_ID;

    control u_control (
        .opcode(ir_ID[6:0]), .aux(ir_ID[14:12]), .func(ir_ID[31:25]), 
        .we_reg(we_reg_ID), .we_mem(we_mem_ID), .mem_to_reg(mem_to_reg_ID), 
        .alu_src(alu_src_ID), .alu_op(alu_op_ID), .branch(branch_ID), .jump(jump_ID)
    );
    
    wire [31:0] write_data_WB;

    regfile u_regfile (
        .clk(~clk), 
        .regwrite(we_reg_WB), .rs1(rs1_ID), .rs2(rs2_ID), .rd(rd_WB), 
        .write_data(write_data_WB), .read_data_1(regA_ID), .read_data_2(regB_ID)
    );

    imm_gen u_imm_gen (
        .inst_in(ir_ID), .imm_out(imm_ID)
    );

    wire [1:0] ID_ForwardA, ID_ForwardB;
    wire [31:0] cmp_A = (ID_ForwardA == 2'b10) ? alu_res_ME : (ID_ForwardA == 2'b01) ? write_data_WB : regA_ID;
    wire [31:0] cmp_B = (ID_ForwardB == 2'b10) ? alu_res_ME : (ID_ForwardB == 2'b01) ? write_data_WB : regB_ID;
    
    // salto si JAL/JALR o si condición de BRANCH ok
    assign Branch_Taken = jump_ID | (branch_ID & (cmp_A == cmp_B)); 
    
    // MUX DirSalto
    wire is_jalr = (ir_ID[6:0] == 7'b1100111);
    wire [31:0] base_salto = is_jalr ? cmp_A : npc_ID;
    
    assign branch_target_ID = (base_salto + imm_ID) & ~32'b1;

    // detector de riesgos
    wire ID_EX_Clr;
    hazard u_hazard (
        .IF_ID_Rs1(rs1_ID), .IF_ID_Rs2(rs2_ID),
        .ID_EX_Rd(rd_EX), .ID_EX_MemRead(mem_to_reg_EX),
        .Branch_Taken(Branch_Taken),
        .PC_En(PC_En), .IF_ID_En(IF_ID_En), .IF_ID_Clr(IF_ID_Clr), .ID_EX_Clr(ID_EX_Clr)
    );

    // ==========================================
    // REGISTRO ID/EX
    // ==========================================
    wire [31:0] pc_EX, pc4_EX, regA_EX, regB_EX, imm_EX;
    wire [4:0] rs1_EX, rs2_EX, rd_EX;
    wire we_reg_EX, we_mem_EX, mem_to_reg_EX, alu_src_EX, branch_EX, jump_EX;
    wire [3:0] alu_op_EX;

    id_ex u_id_ex (
        .clk(clk), .rst(sys_rst), .clr(ID_EX_Clr),
        .we_reg_in(we_reg_ID), .we_mem_in(we_mem_ID), .mem_to_reg_in(mem_to_reg_ID), 
        .alu_src_in(alu_src_ID), .branch_in(branch_ID), .alu_op_in(alu_op_ID), 
        .jump_in(jump_ID), 
        .we_reg_out(we_reg_EX), .we_mem_out(we_mem_EX), .mem_to_reg_out(mem_to_reg_EX), 
        .alu_src_out(alu_src_EX), .branch_out(branch_EX), .alu_op_out(alu_op_EX),
        .jump_out(jump_EX), 
        .pc4_in(pc4_ID), .regA_in(regA_ID), .regB_in(regB_ID), .regC_in(imm_ID),
        .rs1_in(rs1_ID), .rs2_in(rs2_ID), .rd_in(rd_ID),
        .pc4_out(pc4_EX), .regA_out(regA_EX), .regB_out(regB_EX), .regC_out(imm_EX),
        .rs1_out(rs1_EX), .rs2_out(rs2_EX), .rd_out(rd_EX)
    );

    // ==========================================
    // ETAPA EX
    // ==========================================
    wire [1:0] EX_ForwardA, EX_ForwardB;
    wire [31:0] alu_A, alu_B_temp, alu_B, alu_res_EX_raw, alu_res_EX;
    wire alu_zero_EX;
    wire [31:0] alu_res_ME;

    forwarding u_forwarding (
        .ID_EX_Rs1(rs1_EX), .ID_EX_Rs2(rs2_EX), .IF_ID_Rs1(rs1_ID), .IF_ID_Rs2(rs2_ID),
        .EX_ME_Rd(rd_ME), .EX_ME_RegWrite(we_reg_ME), .ME_WB_Rd(rd_WB), .ME_WB_RegWrite(we_reg_WB),
        .EX_ForwardA(EX_ForwardA), .EX_ForwardB(EX_ForwardB),
        .ID_ForwardA(ID_ForwardA), .ID_ForwardB(ID_ForwardB)
    );

    assign alu_A      = (EX_ForwardA == 2'b10) ? alu_res_ME : (EX_ForwardA == 2'b01) ? write_data_WB : regA_EX;
    assign alu_B_temp = (EX_ForwardB == 2'b10) ? alu_res_ME : (EX_ForwardB == 2'b01) ? write_data_WB : regB_EX;
    assign alu_B      = (alu_src_EX) ? imm_EX : alu_B_temp;

    alu u_alu (
        .A(alu_A), .B(alu_B), .sel(alu_op_EX), .R(alu_res_EX_raw), .zero(alu_zero_EX)
    );

    // MUX post-ALU para guardar PC+4 si es un salto (JAL/JALR)
    assign alu_res_EX = jump_EX ? pc4_EX : alu_res_EX_raw;

    // ==========================================
    // REGISTRO EX/ME
    // ==========================================
    wire [31:0] regB_ME, pc4_ME;
    wire [4:0] rd_ME;
    wire we_reg_ME, we_mem_ME, mem_to_reg_ME;

    ex_me u_ex_me (
        .clk(clk), .rst(sys_rst),
        .we_reg_in(we_reg_EX), .we_mem_in(we_mem_EX), .mem_to_reg_in(mem_to_reg_EX),
        .we_reg_out(we_reg_ME), .we_mem_out(we_mem_ME), .mem_to_reg_out(mem_to_reg_ME),
        .alu_in(alu_res_EX), .regB_in(alu_B_temp), .pc4_in(pc4_EX), .rd_in(rd_EX),
        .alu_out(alu_res_ME), .regB_out(regB_ME), .pc4_out(pc4_ME), .rd_out(rd_ME)
    );

    // ==========================================
    // ETAPA ME & WB
    // ==========================================
    
    // --- hacemos un apaño pa poder sacar cosas a la UART (MMIO) ---
    wire is_uart = (alu_res_ME == 32'hFFFFFFFC);
    wire dmem_we = we_mem_ME & ~is_uart; // si va pa la UART no escribimos en memoria
    wire [7:0] uart_tx_data;
    wire uart_tx_en;

    assign uart_tx_en = we_mem_ME & is_uart;
    assign uart_tx_data = regB_ME[7:0];
    
    uart_tx u_tx (
        .clk(clk),
        .data_in(uart_tx_data),
        .tx_en(uart_tx_en),
        .tx(tx)
    );
    // -------------------------------------------------------------- 
    
    wire [31:0] mem_data_ME;
    dmem u_dmem (
        .clk(clk), .we(dmem_we), .address(alu_res_ME), 
        .write_data(regB_ME), .mem_data_out(mem_data_ME)
    );

    wire [31:0] alu_res_WB, mem_data_WB, pc4_WB;
    wire [4:0] rd_WB;
    wire we_reg_WB, mem_to_reg_WB;

    me_wb u_me_wb (
        .clk(clk), .rst(sys_rst),
        .we_reg_in(we_reg_ME), .mem_to_reg_in(mem_to_reg_ME),
        .we_reg_out(we_reg_WB), .mem_to_reg_out(mem_to_reg_WB),
        .alu_in(alu_res_ME), .mem_data_in(mem_data_ME), .pc4_in(pc4_ME), .rd_in(rd_ME),
        .alu_out(alu_res_WB), .mem_data_out(mem_data_WB), .pc4_out(pc4_WB), .rd_out(rd_WB)
    );

    assign write_data_WB = (mem_to_reg_WB) ? mem_data_WB : alu_res_WB;
    
    // ==========================================
    // DEBUG
    // ==========================================
    assign leds[0] = Branch_Taken; // se enciende cuando hay salto
    assign leds[1] = ID_EX_Clr; // se enciende en bloqueos
    
endmodule