`timescale 1ns / 1ps
`include "cpu.h"

module id_stage(
    input                          clk,
    input                          reset,
    input                          ds_flush,
    
    input                          es_allowin,
    output                         ds_allowin,
    
    input                          fs_to_ds_valid,
    input  [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus,
    
    output                         ds_to_es_valid,
    output [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus,
    
    output [`DS_TO_FW_BUS_WD -1:0] ds_to_fw_bus,
    
    input                          stall,
    output [`DS_TO_LU_BUS_WD -1:0] ds_to_lu_bus,
    
    input  [`WS_TO_RF_BUS_WD -1:0] ws_to_rf_bus,
    
    output                         dh_flush,
    output                         dh_stall,
    
    input  [ 4                 :0] rf_raddr,
    output [31                 :0] rf_rdata
    );
    
    reg         ds_valid;
    wire        ds_ready_go;
    
    wire [31                 :0] fs_pc;
    reg  [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus_r;
    assign fs_pc = fs_to_ds_bus[31:0];
    
    wire [31:0] ds_instr;
    wire [31:0] ds_pc;
    assign {ds_instr, 
            ds_pc   } = fs_to_ds_bus_r;
            
    wire        rf_we;
    wire [ 4:0] rf_waddr;
    wire [31:0] rf_wdata;
    assign {rf_we   ,  //37:37
            rf_waddr,  //36:32
            rf_wdata   //31:0
           }            = ws_to_rf_bus;
    
    wire [ 4:0] ExtOp;
    wire [ 1:0] ALUAsrc;
    wire [ 2:0] ALUBsrc;
    wire [10:0] ALUctr;
    wire [ 5:0] Branch;
    wire [ 1:0] Jump;
    wire        MemtoReg;
    wire        MemWr;
    wire        RegWr;
    wire [ 4:0] Load;
    wire [ 2:0] Store;
    wire        shamt;
    wire [31:0] imm;
    wire [31:0] rs1_value;
    wire [31:0] rs2_value;
    
    wire [ 6:0] op;
    wire [ 6:0] func7;
    wire [ 2:0] func3;
    wire [ 4:0] rs2;
    wire [ 4:0] rs1;
    wire [ 4:0] rd;
    wire [31:0] immI;
    wire [31:0] immS;
    wire [31:0] immB;
    wire [31:0] immU;
    wire [31:0] immJ;
    
    wire [  4:0] rf_raddr1;
    wire [ 31:0] rf_rdata1;
    wire [  4:0] rf_raddr2;
    wire [ 31:0] rf_rdata2;
    
    assign ds_to_es_bus= { ALUctr      ,  //177:167
                           shamt       ,  //166:166
                           Load        ,  //166:162
                           Store       ,  //161:159
                           MemWr       ,  //158:158
                           Branch      ,  //157:152
                           Jump        ,  //151:150
                           MemtoReg    ,  //149:149
                           RegWr       ,  //148:148
                           ALUAsrc     ,  //147:146
                           ALUBsrc     ,  //145:143
                           rd          ,  //142:138
                           rs1         ,  //137:133
                           rs2         ,  //132:128
                           imm         ,  //127:96
                           rs1_value   ,  //95 :64
                           rs2_value   ,  //63 :32
                           ds_pc          //31 :0
                          };
                          
    assign ds_to_fw_bus = {rs1, rs2};
    
    assign ds_to_lu_bus = {rs1, rs2};
    
    assign ds_ready_go    = !stall;
    assign ds_allowin     = !ds_valid || ds_ready_go && es_allowin;
    assign ds_to_es_valid = ds_valid && ds_ready_go;
    always @(posedge clk) begin
        if (reset) begin
            ds_valid <= 1'b0;
        end
        else if (ds_allowin) begin
            ds_valid <= fs_to_ds_valid;    
        end
        
        if (reset || ds_flush) begin
            fs_to_ds_bus_r <= 0;
        end
        else if (fs_to_ds_valid && ds_allowin) begin
            fs_to_ds_bus_r <= fs_to_ds_bus;
        end
    end
    
    assign op    = ds_instr[ 6: 0];
    assign func7 = ds_instr[31:25];
    assign func3 = ds_instr[14:12];
    assign rs2   = ds_instr[24:20];
    assign rs1   = ds_instr[19:15]; 
    assign rd    = ds_instr[11: 7];
    assign immI  = {{20{ds_instr[31]}}, ds_instr[31:20]};
    assign immU  = { ds_instr[31:12]  , 12'b0};
    assign immS  = {{20{ds_instr[31]}}, ds_instr[31:25], ds_instr[11: 7]};
    assign immB  = {{20{ds_instr[31]}}, ds_instr[7]    , ds_instr[30:25], ds_instr[11: 8], 1'b0};
    assign immJ  = {{12{ds_instr[31]}}, ds_instr[19:12], ds_instr[20]   , ds_instr[30:21], 1'b0};

    ctrsignal u_ctrsignal(
        .OP(op),
        .func3    (func3   ),
        .func7    (func7   ),
        
        .ExtOp    (ExtOp   ),
        .ALUAsrc  (ALUAsrc ),
        .ALUBsrc  (ALUBsrc ),
        .ALUctr   (ALUctr  ),
        .RegWr    (RegWr   ),
        .MemtoReg (MemtoReg),
        .MemWr    (MemWr   ),
        .Branch   (Branch  ),
        .Jump     (Jump    ),
        .Load     (Load    ),
        .Store    (Store   ),
        .shamt    (shamt   )
    );
    
    assign imm       = ({32{ExtOp[4]}} & immU)
                     | ({32{ExtOp[3]}} & immJ)
                     | ({32{ExtOp[2]}} & immS)
                     | ({32{ExtOp[1]}} & immI)
                     | ({32{ExtOp[0]}} & immB);
    
    assign rf_raddr1 = rs1;
    assign rf_raddr2 = rs2;
    regfile u_regfile(
        .clk    (clk      ),
        .reset  (reset    ),
        .raddr1 (rf_raddr1),
        .rdata1 (rf_rdata1),
        .raddr2 (rf_raddr2),
        .rdata2 (rf_rdata2),
        .raddr3 (rf_raddr ),
        .rdata3 (rf_rdata ),
        .we     (rf_we    ),
        .waddr  (rf_waddr ),
        .wdata  (rf_wdata )
        );
    assign rs1_value = rf_rdata1;
    assign rs2_value = rf_rdata2;
    
    assign dh_flush  = ^Jump || ^Branch;
    assign dh_stall  = ^Branch;
endmodule
