// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this 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.
//
//-------------------------------------------------------------------------------
//-- Title      : Claim - Complete - Tracker
//-------------------------------------------------------------------------------
//-- File       : plic_claim_complete_tracker.sv
//-- Author     : Gian Marti      <gimarti.student.ethz.ch>
//-- Author     : Thomas Kramer   <tkramer.student.ethz.ch>
//-- Author     : Thomas E. Benz  <tbenz.student.ethz.ch>
//-- Company    : Integrated Systems Laboratory, ETH Zurich
//-- Created    : 2018-03-31
//-- Last update: 2018-03-31
//-- Platform   : ModelSim (simulation), Synopsys (synthesis)
//-- Standard   : SystemVerilog IEEE 1800-2012
//-------------------------------------------------------------------------------
//-- Description: Implements the control logic of the plic
//-------------------------------------------------------------------------------
//-- Revisions  :
//-- Date        Version  Author  Description
//-- 2018-03-31  2.0      tbenz   Created header
//-------------------------------------------------------------------------------

//FSM that receives interrupt claims and interrupt completes from targets
//and generates the fitting inerrupt claims and interrupt completes for sources
module plic_claim_complete_tracker #(
    parameter int NUM_TARGETS  =  1,
    parameter int NUM_GATEWAYS =  1,
    parameter int ID_BITWIDTH  =  4
)(
    input  logic                   clk_i,   // Clock
    input  logic                   rst_ni,  // Asynchronous reset active low

    input  logic [ID_BITWIDTH-1:0] identifier_of_largest_priority_per_target[NUM_TARGETS],
    input  logic                   target_irq_claims_i   [NUM_TARGETS],
    input  logic                   target_irq_completes_i[NUM_TARGETS],
    input  logic [ID_BITWIDTH-1:0] target_irq_completes_identifier_i[NUM_TARGETS],

    output logic                   gateway_irq_claims_o   [NUM_GATEWAYS],
    output logic                   gateway_irq_completes_o[NUM_GATEWAYS]

);

    // claimed_gateways_q[target] is 0 if the target has not claimed the irq of any gateway
    // and the is the identifier of the claimed gateway otherwise
    logic [ID_BITWIDTH-1:0] claimed_gateways_q[NUM_TARGETS];

    // the +1 is because counting starts from 1 and goes to NUM_GATEWAYS+1
    logic                   claim_array         [NUM_GATEWAYS+1][NUM_TARGETS];
    logic                   save_claims_array_q [NUM_GATEWAYS+1][NUM_TARGETS];
    logic                   complete_array      [NUM_GATEWAYS+1][NUM_TARGETS];

    logic [ID_BITWIDTH-1:0] complete_id;


    // for handling claims
    for (genvar counter = 0; counter < NUM_TARGETS; counter++) begin
        always_ff @(posedge clk_i or negedge rst_ni) begin : proc_target
            integer id;

            if (~rst_ni) begin
                claimed_gateways_q[counter]         <= '0;

                for (integer i = 0; i <= NUM_GATEWAYS; i++) begin
                    claim_array[i][counter]         <= '0;
                    save_claims_array_q[i][counter] <= '0;
                end

            end else begin
                // per default, all claims and completes are zero
                for (integer i = 0; i <= NUM_GATEWAYS; i++) begin
                    claim_array[i][counter]    <= 0;
                    complete_array[i][counter] <= 0;
                end

                // if a claim is issued, forward it to gateway with highest priority for the claiming target
                if (target_irq_claims_i[counter]) begin
                    id                                = identifier_of_largest_priority_per_target[counter];
                    claim_array[id][counter]         <= 1;

                    // save claim for later when the complete-notification arrives
                    save_claims_array_q[id][counter] <= 1;

                end else begin
                    // if a complete is issued, check if that gateway has previously been claimed by
                    // this target and forward the
                    // complete message to that gateway. if no claim has previously been issued, the
                    // complete message is ignored
                    // integer complete_id = target_irq_completes_identifier_i[counter];
                    complete_id = target_irq_completes_identifier_i[counter];

                    if (target_irq_completes_i[counter] && (save_claims_array_q[complete_id][counter] > 0)) begin
                        complete_array[complete_id][counter]      <= 1;
                        save_claims_array_q[complete_id][counter] <= 0;
                    end
                end
            end
        end
    end


    // the outputs for an id are the ORs of all targets for that id
    always_comb begin : proc_result_computation
        for (integer gateway = 1; gateway <= NUM_GATEWAYS; gateway++) begin
            automatic logic is_claimed   = '0;
            automatic logic is_completed = '0;

            for (integer target = 0; target < NUM_TARGETS; target++) begin
                is_claimed   = is_claimed   | claim_array   [gateway][target];
                is_completed = is_completed | complete_array[gateway][target];
            end

            if (is_claimed) begin
                gateway_irq_claims_o   [gateway-1] = 1;
            end else begin
                gateway_irq_claims_o   [gateway-1] = 0;
            end

            if (is_completed) begin
                gateway_irq_completes_o[gateway-1] = 1;
            end else begin
                gateway_irq_completes_o[gateway-1] = 0;
            end
        end
    end

endmodule //plic_claim_complete_tracker