// 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      : PLIC Core
//-- File       : plic_core.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: PLIC Top-level
//-------------------------------------------------------------------------------
//-- Revisions  :
//-- Date        Version  Author  Description
//-- 2018-03-31  2.0      tbenz   Created header
//-------------------------------------------------------------------------------

module plic #(
    parameter int ADDR_WIDTH         = 32,   // can be either 32 or 64 bits (don't use 64bit at the moment as this causes memory map issues)
    parameter int DATA_WIDTH         = 32,   // can be either 32 or 64 bits (don't use 64bit at the moment as this causes memory map issues)
    parameter int ID_BITWIDTH        = -1,   // width of the gateway identifiers
    parameter int PARAMETER_BITWIDTH = -1,   // width of the internal parameter e.g. priorities
    parameter int NUM_TARGETS        = -1,   // number of target slices
    parameter int NUM_SOURCES        = -1    // number of sources = number of gateways
) (
    input  logic                   clk_i,
    input  logic                   rst_ni,
    input  logic [NUM_SOURCES-1:0] irq_sources_i,
    output logic [NUM_TARGETS-1:0] eip_targets_o,
    REG_BUS.in                     external_bus_io
);
    // declare all local variables
    // gateway arrays always go from NUM_SOURCES to 1 because gateway ids start at 1
    logic                          gateway_irq_pendings    [NUM_SOURCES];               //for pending irqs of the gateways
    logic                          gateway_claimed         [NUM_SOURCES];               //if a gateway is claimed, it masks its irq
    logic                          gateway_completed       [NUM_SOURCES];               //if a gateway is completed, it is reenabled
    logic [ID_BITWIDTH-1:0       ] gateway_ids             [NUM_SOURCES];               //ids of gateways
    logic [PARAMETER_BITWIDTH-1:0] gateway_priorities      [NUM_SOURCES];               //priorities of gateways

    logic                          irq_enableds            [NUM_SOURCES][NUM_TARGETS];
    logic [PARAMETER_BITWIDTH-1:0] target_thresholds       [NUM_TARGETS];
    logic [ID_BITWIDTH-1:0       ] identifier_of_largest_priority_per_target [NUM_TARGETS];

    logic                          target_irq_claims       [NUM_TARGETS];
    logic                          target_irq_completes    [NUM_TARGETS];
    logic [ID_BITWIDTH-1:0       ] target_irq_completes_id [NUM_TARGETS];

    //instantiate and connect gateways
    for (genvar counter = 0; counter < NUM_SOURCES; counter++) begin : gen_plic_gateway
        plic_gateway plic_gateway_instance (
            .clk_i         ( clk_i                         ),
            .rst_ni        ( rst_ni                        ),
            .irq_source_i  ( irq_sources_i       [counter] ),
            .claim_i       ( gateway_claimed     [counter] ),
            .completed_i   ( gateway_completed   [counter] ),
            .irq_pending_o ( gateway_irq_pendings[counter] )
        );
    end

    // assign ids to gateways
    for (genvar counter = 1; counter <= NUM_SOURCES; counter++) begin
        assign gateway_ids[counter-1] = counter;

    end

    // instantiate and connect target slices
    for (genvar counter = 0; counter < NUM_TARGETS; counter++) begin : gen_plic_target_slice

        logic irq_enableds_slice[NUM_SOURCES];
        for (genvar inner_counter = 0; inner_counter < NUM_SOURCES; inner_counter++) begin
            assign irq_enableds_slice[inner_counter] = irq_enableds[inner_counter][counter];
        end

        plic_target_slice #(
            .PRIORITY_BITWIDTH ( PARAMETER_BITWIDTH ),
            .ID_BITWIDTH       ( ID_BITWIDTH        ),
            .NUM_GATEWAYS      ( NUM_SOURCES        )
        ) plic_target_slice_instance (
            .interrupt_pending_i     ( gateway_irq_pendings                               ),
            .interrupt_priority_i    ( gateway_priorities                                 ),
            .interrupt_id_i          ( gateway_ids                                        ),
            .interrupt_enable_i      ( irq_enableds_slice                                 ),
            .threshold_i             ( target_thresholds[counter]                         ),
            .ext_interrupt_present_o ( eip_targets_o[counter]                             ),
            .identifier_of_largest_o ( identifier_of_largest_priority_per_target[counter] )
        );
    end

    //instantiate and connect plic_interface
    plic_interface #(
        .ADDR_WIDTH         ( ADDR_WIDTH         ),
        .DATA_WIDTH         ( DATA_WIDTH         ),
        .ID_BITWIDTH        ( ID_BITWIDTH        ),
        .PARAMETER_BITWIDTH ( PARAMETER_BITWIDTH ),
        .NUM_TARGETS        ( NUM_TARGETS        ),
        .NUM_GATEWAYS       ( NUM_SOURCES        )
    ) plic_interface_instance (
        .clk_i,
        .rst_ni,
        .id_of_largest_priority_i   ( identifier_of_largest_priority_per_target ),
        .pending_array_i            ( gateway_irq_pendings                      ),
        .thresholds_o               ( target_thresholds                         ),
        .gateway_priorities_o       ( gateway_priorities                        ),
        .irq_enables_o              ( irq_enableds                              ),
        .target_irq_claims_o        ( target_irq_claims                         ),
        .target_irq_completes_o     ( target_irq_completes                      ),
        .target_irq_completes_id_o  ( target_irq_completes_id                   ),
        .external_bus_io            ( external_bus_io                           )
    );

    //instantiate and connect claim_complete_tracker
    plic_claim_complete_tracker #(
        .NUM_TARGETS  ( NUM_TARGETS ),
        .NUM_GATEWAYS ( NUM_SOURCES ),
        .ID_BITWIDTH  ( ID_BITWIDTH )
    ) plic_claim_complete_tracker_instance (
        .clk_i                                     ( clk_i                                     ),
        .rst_ni                                    ( rst_ni                                    ),
        .identifier_of_largest_priority_per_target ( identifier_of_largest_priority_per_target ),
        .target_irq_claims_i                       ( target_irq_claims                         ),
        .target_irq_completes_i                    ( target_irq_completes                      ),
        .target_irq_completes_identifier_i         ( target_irq_completes_id                   ),
        .gateway_irq_claims_o                      ( gateway_claimed                           ),
        .gateway_irq_completes_o                   ( gateway_completed                         )
    );

    //pragma translate_off
    `ifndef VERILATOR
    initial begin
        assert((ADDR_WIDTH == 32) | (ADDR_WIDTH == 64)) else $error("Address width has to bei either 32 or 64 bit");
        assert((DATA_WIDTH == 32) | (DATA_WIDTH == 64)) else $error("Data width has to bei either 32 or 64 bit");
        assert(ID_BITWIDTH > 0       )                  else $error("ID_BITWIDTH has to be larger than 1");
        assert(ID_BITWIDTH < 10      )                  else $error("ID_BITWIDTH has to be smaller than 10");
        assert(PARAMETER_BITWIDTH > 0)                  else $error("PARAMETER_BITWIDTH has to be larger than 1");
        assert(PARAMETER_BITWIDTH < 8)                  else $error("PARAMETER_BITWIDTH has to be smaller than 8");
        assert(NUM_SOURCES > 0       )                  else $error("Num od Gateways has to be larger than 1");
        assert(NUM_SOURCES < 512     )                  else $error("Num of Gateways has to be smaller than 512");
        assert(NUM_TARGETS > 0       )                  else $error("Num Target slices has to be larger than 1");
        assert(NUM_TARGETS < 15872   )                  else $error("Num target slices has to be smaller than 15872");
    end
    `endif
    //pragma translate_on
endmodule