// 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      : Target Slice
//-- File       : plic_target_slice.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: Target Slice
//-------------------------------------------------------------------------------
//-- Revisions  :
//-- Date        Version  Author  Description
//-- 2018-03-31  2.0      tbenz   Created header
//-------------------------------------------------------------------------------

// Note: The gateways are expected to be ordered by their IDs (ascending).
// This resolves priority ties by choosing the gateway with the lower ID.
module plic_target_slice #(
    parameter int PRIORITY_BITWIDTH = 8,
    parameter int ID_BITWIDTH       = 8,
    parameter int NUM_GATEWAYS      = 1
)(
    // Input signals from gateways.
    input  logic                          interrupt_pending_i [NUM_GATEWAYS],
    input  logic [PRIORITY_BITWIDTH-1:0]  interrupt_priority_i[NUM_GATEWAYS],
    input  logic [ID_BITWIDTH-1:0      ]  interrupt_id_i      [NUM_GATEWAYS],
    input  logic                          interrupt_enable_i  [NUM_GATEWAYS],
    input  logic [PRIORITY_BITWIDTH-1:0]  threshold_i,
    output logic                          ext_interrupt_present_o,
    output logic [ID_BITWIDTH-1:0      ]  identifier_of_largest_o
);

    logic[PRIORITY_BITWIDTH:0]  interrupt_priority_masked[NUM_GATEWAYS];


    // Signals that represent the selected interrupt source.
    logic[PRIORITY_BITWIDTH:0] best_priority;
    logic[ID_BITWIDTH-1:0    ] best_id;

    // Create a tree to find the best interrupt source.
    plic_find_max #(
        .NUM_OPERANDS             ( NUM_GATEWAYS              ),
        .ID_BITWIDTH              ( ID_BITWIDTH               ),
        .PRIORITY_BITWIDTH        ( PRIORITY_BITWIDTH + 1     )
    ) find_max_instance (
        .priorities_i             ( interrupt_priority_masked ),
        .identifiers_i            ( interrupt_id_i            ),
        // Outputs
        .largest_priority_o       ( best_priority             ),
        .identifier_of_largest_o  ( best_id                   )
    );

    // Compare the priority of the best interrupt source to the threshold.
    always_comb begin : proc_compare_threshold
        if ((best_priority - 1 > threshold_i) && (best_priority != '0)) begin
            ext_interrupt_present_o = 1;
            identifier_of_largest_o = best_id;
        end else begin
            if ((best_priority - 1 <= threshold_i) && (best_priority != '0)) begin
                ext_interrupt_present_o = 0;
                identifier_of_largest_o = best_id;
            end else begin
                ext_interrupt_present_o = 0;
                identifier_of_largest_o = 0;
            end
        end
    end

    always_comb begin : proc_mask_gateway_outputs
        for (int i = 0; i < NUM_GATEWAYS; i++) begin
             if (interrupt_enable_i[i] && interrupt_pending_i[i]) begin
                interrupt_priority_masked[i] = interrupt_priority_i[i] + 1;      //priority shift +1
             end else begin
                interrupt_priority_masked[i] = '0;
             end
        end
    end
endmodule