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

module forward(
    input  clk,
    
    input  [`DS_TO_FW_BUS_WD - 1:0] ds_to_fw_bus,
    input  [`ES_TO_FW_BUS_WD - 1:0] es_to_fw_bus,
    input  [`MS_TO_FW_BUS_WD - 1:0] ms_to_fw_bus,
    
    
    output [`FW_TO_ES_BUS_WD - 1:0] fw_to_es_bus
    );
    
    wire [4:0] ds_rs1;
    wire [4:0] ds_rs2;
    wire [4:0] es_rs2;
    wire [4:0] es_rd ;
    wire       es_MemWr;
    wire       es_RegWr;
    wire [4:0] ms_rd ;
    wire       ms_RegWr;
    
    

    wire [1:0] BusAFw;
    wire [1:0] BusBFw;
    wire       DiSrc;

    reg  [`FW_TO_ES_BUS_WD - 1:0] fw_to_es_bus_r;
    
   
    assign {ds_rs1, ds_rs2} = ds_to_fw_bus;
    assign {es_rs2, es_rd , es_RegWr, es_MemWr} = es_to_fw_bus;
    assign {ms_rd , ms_RegWr        } = ms_to_fw_bus;

    
    assign BusAFw[0] = ms_RegWr && (ms_rd != 5'b0) && (es_rd != ds_rs1) && (ms_rd == ds_rs1);
    assign BusAFw[1] = es_RegWr && (es_rd != 5'b0) && (es_rd == ds_rs1)                     ;
    assign BusBFw[0] = ms_RegWr && (ms_rd != 5'b0) && (es_rd != ds_rs2) && (ms_rd == ds_rs2);
    assign BusBFw[1] = es_RegWr && (es_rd != 5'b0) && (es_rd == ds_rs2)                     ;
    assign DiSrc     = ms_RegWr && (ms_rd != 5'b0) && (ms_rd == es_rs2) &&  es_MemWr        ;

    always @(posedge clk) begin
        fw_to_es_bus_r <= {BusAFw, BusBFw,  DiSrc};
    end

    assign fw_to_es_bus = fw_to_es_bus_r;

endmodule


module loaduse(
    input                          clk,
    input                          reset,
    
    input  [`DS_TO_LU_BUS_WD -1:0] ds_to_lu_bus,
    input  [`ES_TO_LU_BUS_WD -1:0] es_to_lu_bus,
    
    output                         ds_stall,
    output                         es_flush
    );
    
    wire [4:0] ds_sr1;
    wire [4:0] ds_sr2;
    wire [4:0] es_Load;
    wire [4:0] es_rd;
    
    wire stall;
    
    assign {ds_sr1, ds_sr2 } = ds_to_lu_bus;
    assign {es_rd , es_Load} = es_to_lu_bus;
    
    assign stall = ^es_Load && 
                 (((ds_sr1 == es_rd) && (ds_sr1 != 5'b0)) || ((ds_sr2 == es_rd) && (ds_sr2 != 5'b0)));
    
    assign ds_stall = stall;
    assign es_flush = stall;
endmodule


module piplinectr(
    input  clk,
    input  reset,
    input  lu_flush,
    input  dh_flush,
    input  dh_stall,
    
    output fs_stall,
    output fs_flush,
    output ds_flush,
    output es_flush,
    output ms_flush,
    output ws_flush
    );
    
    reg [7:0] flush;
    reg [1:0] stall;
    
    always @(posedge clk) begin 
        if(reset) begin 
            flush <= 5'b0;
        end
        else if(lu_flush) begin 
            flush <= {flush[6]  , lu_flush     , flush[4:0], 1'b0};
        end 
        else if(dh_flush) begin
            flush <= {flush[6:5], {3{dh_flush}}, flush[1:0], 1'b0};
        end
        else begin
            flush <= {flush[6:0]                           , 1'b0};
        end
        
        if(reset) begin
            stall <= 2'b0;
        end
        else if(dh_stall) begin
            stall <= {stall[0], dh_stall};
        end
        else begin
            stall <= {stall[0], 1'b0};
        end
    end
    
    
    assign {ws_flush,
            ms_flush, 
            es_flush, 
            ds_flush, 
            fs_flush} = flush[7:3] | {2'b0, lu_flush, 2'b0} | {3'b0, dh_flush, dh_flush};
            
    assign fs_stall   = ^stall | dh_stall;
endmodule



