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

module if_stage(
    input                          clk,
    input                          reset,
    input                          fs_flush,
    input                          fs_stall,
    
    input                          ds_allowin,
    input  [`BR_BUS_WD       -1:0] br_bus,
    
    output                         fs_to_ds_valid,
    output [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus,
    
    output        instr_ram_en,
    output [ 3:0] instr_ram_wen,
    output [31:0] instr_ram_addr,
    output [31:0] instr_ram_wdata,
    input  [31:0] instr_ram_rdata
    );
    
    reg         fs_valid;
    wire        fs_ready_go;
    wire        fs_allowin;
    wire        to_fs_valid;
    
    wire [31:0] seq_pc;
    wire [31:0] nextpc;
    
    wire        br_taken;
    wire [31:0] br_target;
    assign {br_taken, br_target} = br_bus;
    
    
    wire [31:0] fs_instr;
    reg  [31:0] fs_pc;
    assign fs_to_ds_bus = {fs_instr, fs_pc};
    
    assign to_fs_valid  = ~reset;
    assign seq_pc = fs_pc + 3'h4;
    assign nextpc = br_taken ? br_target : 
                    fs_stall ? fs_pc     :
                               seq_pc; 
    
    // IF stage
    assign fs_ready_go    = !(fs_flush);
    assign fs_allowin     = !fs_valid || fs_ready_go && ds_allowin;
    assign fs_to_ds_valid =  fs_valid && fs_ready_go;
    always @(posedge clk) begin
        if (reset) begin
            fs_valid <= 1'b0;
        end
        else if (fs_allowin) begin
            fs_valid <= to_fs_valid;
        end
    
        if (reset || (fs_flush && !fs_stall)) begin
            fs_pc <= 32'hfffffffc;
        end
        else if (to_fs_valid && fs_allowin) begin
            fs_pc <= nextpc;
        end
    end

    assign instr_ram_en    = to_fs_valid && fs_allowin;
    assign instr_ram_wen   = 4'h0;
    assign instr_ram_addr  = nextpc;
    assign instr_ram_wdata = 32'b0;

    
    assign fs_instr        = instr_ram_rdata;
endmodule
