// Copyright © 2019-2023
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

`include "VX_define.vh"
`include "VX_trace.vh"

module VX_issue import VX_gpu_pkg::*; #(
    parameter CORE_ID = 0
) (
    `SCOPE_IO_DECL

    input wire              clk,
    input wire              reset,

`ifdef PERF_ENABLE
    VX_pipeline_perf_if.issue perf_issue_if,
`endif

    VX_decode_if.slave      decode_if,
    VX_writeback_if.slave   writeback_if [`ISSUE_WIDTH],

    VX_dispatch_if.master   alu_dispatch_if [`ISSUE_WIDTH],
    VX_dispatch_if.master   lsu_dispatch_if [`ISSUE_WIDTH],
`ifdef EXT_F_ENABLE
    VX_dispatch_if.master   fpu_dispatch_if [`ISSUE_WIDTH],
`endif
`ifdef EXT_T_ENABLE
    VX_dispatch_if.master   tensor_dispatch_if [`ISSUE_WIDTH],
`ifdef EXT_T_ASYNC
    VX_tc_rf_if.slave       tensor_regfile_if,
`endif
`endif
    VX_dispatch_if.master   sfu_dispatch_if [`ISSUE_WIDTH]
);
    VX_ibuffer_if  ibuffer_if [`ISSUE_WIDTH]();
    VX_ibuffer_if  scoreboard_if [`ISSUE_WIDTH]();
    VX_operands_if operands_if [`ISSUE_WIDTH]();

    `RESET_RELAY (ibuf_reset, reset);
    `RESET_RELAY (scoreboard_reset, reset);
    `RESET_RELAY (operands_reset, reset);
    `RESET_RELAY (dispatch_reset, reset);

    VX_ibuffer #(
        .CORE_ID (CORE_ID)
    ) ibuffer (
        .clk            (clk),
        .reset          (ibuf_reset), 
        .decode_if      (decode_if),
        .ibuffer_if     (ibuffer_if)
    );

    VX_scoreboard #(
        .CORE_ID (CORE_ID)
    ) scoreboard (
        .clk            (clk),
        .reset          (scoreboard_reset),
    `ifdef PERF_ENABLE
        .perf_scb_stalls(perf_issue_if.scb_stalls),
        .perf_scb_any_unit_uses(perf_issue_if.scb_any_unit_uses),
        .perf_scb_fires (perf_issue_if.scb_fires),
        .perf_scb_any_fire_cycles (perf_issue_if.scb_any_fire_cycles),
        .perf_units_uses(perf_issue_if.units_uses),
        .perf_sfu_uses  (perf_issue_if.sfu_uses),
    `endif
        .writeback_if   (writeback_if),
        .ibuffer_if     (ibuffer_if),
        .scoreboard_if  (scoreboard_if)
    );

`ifdef GPR_DUPLICATED
    VX_operands_dup #(
`else
    VX_operands #(
`endif
        .CORE_ID (CORE_ID),
        .CACHE_ENABLE (0)
    ) operands (
        .clk            (clk), 
        .reset          (operands_reset), 
        .writeback_if   (writeback_if),
        .scoreboard_if  (scoreboard_if),
`ifdef EXT_T_ASYNC
        .operands_if    (operands_if),
        .tensor_regfile_if (tensor_regfile_if)
`else
        .operands_if    (operands_if)
`endif
    );

    VX_dispatch #(
        .CORE_ID (CORE_ID)
    ) dispatch (
        .clk            (clk), 
        .reset          (dispatch_reset),
    `ifdef PERF_ENABLE
        .perf_stalls    (perf_issue_if.dispatch_stalls),
        .perf_valids    (perf_issue_if.dispatch_valids),
        .perf_fires     (perf_issue_if.dispatch_fires),
        .perf_any_fire_cycles (perf_issue_if.dispatch_any_fire_cycles),
    `endif
        .operands_if    (operands_if),
        .alu_dispatch_if(alu_dispatch_if),
        .lsu_dispatch_if(lsu_dispatch_if),
    `ifdef EXT_F_ENABLE
        .fpu_dispatch_if(fpu_dispatch_if),
    `endif
    `ifdef EXT_T_ENABLE
        .tensor_dispatch_if(tensor_dispatch_if),
    `endif
        .sfu_dispatch_if(sfu_dispatch_if)
    ); 

`ifdef SIMULATION
`ifdef DBG_TRACE_CORE_PIPELINE_VCS
    for (genvar i = 0; i < `ISSUE_WIDTH; ++i) begin
        wire ibuf_probe_pc = ibuffer_if[i].valid
                           && (ibuffer_if[i].data.PC >= 32'h80000240)
                           && (ibuffer_if[i].data.PC <= 32'h80000260);
        wire scb_probe_pc = scoreboard_if[i].valid
                          && (scoreboard_if[i].data.PC >= 32'h80000240)
                          && (scoreboard_if[i].data.PC <= 32'h80000260);
        wire ops_probe_pc = operands_if[i].valid
                          && (operands_if[i].data.PC >= 32'h80000240)
                          && (operands_if[i].data.PC <= 32'h80000260);

        always @(posedge clk) begin
            if (!reset && (CORE_ID == 0) && ($time > `TRACE_STARTTIME) && (ibuf_probe_pc || scb_probe_pc || ops_probe_pc)) begin
                `TRACE(2, ("%d: core%0d-issue-probe: isw=%0d, ibuf=%b/%b PC=0x%0h ex=0x%0h op=0x%0h, scb=%b/%b PC=0x%0h ex=0x%0h op=0x%0h, ops=%b/%b PC=0x%0h ex=0x%0h op=0x%0h, alu=%b/%b lsu=%b/%b sfu=%b/%b (#ibuf=%0d #scb=%0d #ops=%0d)\n",
                    $time, CORE_ID, i,
                    ibuffer_if[i].valid, ibuffer_if[i].ready, ibuffer_if[i].data.PC, ibuffer_if[i].data.ex_type, ibuffer_if[i].data.op_type,
                    scoreboard_if[i].valid, scoreboard_if[i].ready, scoreboard_if[i].data.PC, scoreboard_if[i].data.ex_type, scoreboard_if[i].data.op_type,
                    operands_if[i].valid, operands_if[i].ready, operands_if[i].data.PC, operands_if[i].data.ex_type, operands_if[i].data.op_type,
                    alu_dispatch_if[i].valid, alu_dispatch_if[i].ready,
                    lsu_dispatch_if[i].valid, lsu_dispatch_if[i].ready,
                    sfu_dispatch_if[i].valid, sfu_dispatch_if[i].ready,
                    ibuffer_if[i].data.uuid, scoreboard_if[i].data.uuid, operands_if[i].data.uuid));
            end
        end
    end
`endif
`endif

`ifdef DBG_SCOPE_ISSUE
    if (CORE_ID == 0) begin
    `ifdef SCOPE
        wire operands_if_fire = operands_if[0].valid && operands_if[0].ready;
        wire operands_if_not_ready = ~operands_if[0].ready;
        wire writeback_if_valid = writeback_if[0].valid;
        VX_scope_tap #(
            .SCOPE_ID (2),
            .TRIGGERW (4),
            .PROBEW   (`UUID_WIDTH + `NUM_THREADS + `EX_BITS + `INST_OP_BITS + `INST_MOD_BITS +
                1 + `NR_BITS + `XLEN + 1 + 1 + (`NUM_THREADS * 3 * `XLEN) +
                `UUID_WIDTH + `NUM_THREADS + `NR_BITS + (`NUM_THREADS*`XLEN) + 1)
        ) scope_tap (
            .clk(clk),
            .reset(scope_reset),
            .start(1'b0),
            .stop(1'b0),
            .triggers({
                reset, 
                operands_if_fire,
                operands_if_not_ready, 
                writeback_if_valid
            }),
            .probes({
                operands_if[0].data.uuid,
                operands_if[0].data.tmask,
                operands_if[0].data.ex_type,
                operands_if[0].data.op_type,
                operands_if[0].data.op_mod,
                operands_if[0].data.wb,
                operands_if[0].data.rd,
                operands_if[0].data.imm,
                operands_if[0].data.use_PC,
                operands_if[0].data.use_imm,
                operands_if[0].data.rs1_data,
                operands_if[0].data.rs2_data,
                operands_if[0].data.rs3_data,
                writeback_if[0].data.uuid,
                writeback_if[0].data.tmask,
                writeback_if[0].data.rd,
                writeback_if[0].data.data,
                writeback_if[0].data.eop
            }),
            .bus_in(scope_bus_in),
            .bus_out(scope_bus_out)
        );
    `endif        
    `ifdef CHIPSCOPE
        ila_issue ila_issue_inst (
            .clk    (clk),
            .probe0 ({operands_if.uuid, ibuffer.rs3, ibuffer.rs2, ibuffer.rs1, operands_if.PC, operands_if.tmask, operands_if.wid, operands_if.ex_type, operands_if.op_type, operands_if.ready, operands_if.valid}),
            .probe1 ({writeback_if.uuid, writeback_if.data[0], writeback_if.PC, writeback_if.tmask, writeback_if.wid, writeback_if.eop, writeback_if.valid})
        );
    `endif
    end
`else
    `SCOPE_IO_UNUSED()
`endif

`ifdef PERF_ENABLE
    reg [`PERF_CTR_BITS-1:0] perf_ibf_stalls;
    
    wire decode_stall = decode_if.valid && ~decode_if.ready;

    always @(posedge clk) begin
        if (reset) begin
            perf_ibf_stalls <= '0;
        end else begin
            perf_ibf_stalls <= perf_ibf_stalls + `PERF_CTR_BITS'(decode_stall);
        end
    end

    assign perf_issue_if.ibf_stalls = perf_ibf_stalls;
`endif

endmodule
