// 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.

////////////////////////////////////////////////////////////////////////////////
// Engineer:       Davide Schiavone - pschiavo@iis.ee.ethz.ch                 //
//                                                                            //
// Additional contributions by:                                               //
//                                                                            //
// Design Name:    Interrupt Controller                                       //
// Project Name:   RI5CY                                                      //
// Language:       SystemVerilog                                              //
//                                                                            //
// Description:    Basic Interrupt Controller                                 //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

module cv32e40s_int_controller import cv32e40s_pkg::*;
(
  input  logic        clk,
  input  logic        rst_n,

  // External interrupt lines
  input  logic [31:0] irq_i,                    // Level-triggered interrupt inputs

  // To controller
  output logic        irq_req_ctrl_o,
  output logic  [4:0] irq_id_ctrl_o,
  output logic        irq_wu_ctrl_o,

  // From cs_registers
  input  logic [31:0] mie_i,                    // MIE CSR
  input  mstatus_t    mstatus_i,                // MSTATUS CSR
  input  privlvl_t    priv_lvl_i,               // Current privilege level of core

  // To cs_registers
  output logic [31:0] mip_o                     // MIP CSR
);

  logic        global_irq_enable;
  logic [31:0] irq_local_qual;
  logic [31:0] irq_q;

  // Register all interrupt inputs (on gated clock). The wake-up logic will
  // observe irq_i as well, but in all other places irq_q will be used to
  // avoid timing paths from irq_i to instr_*_o

  always_ff @(posedge clk, negedge rst_n)
  begin
    if (rst_n == 1'b0) begin
      irq_q <= '0;
    end else begin
      irq_q <= irq_i & IRQ_MASK;
    end
  end

  // MIP CSR
  assign mip_o = irq_q;

  // Qualify registered IRQ with MIE CSR to compute locally enabled IRQs
  assign irq_local_qual = irq_q & mie_i;

  // Wake-up signal based on unregistered IRQ such that wake-up can be caused if no clock is present
  assign irq_wu_ctrl_o = |(irq_i & mie_i);

  // Global interrupt enable
  assign global_irq_enable = mstatus_i.mie || (priv_lvl_i < PRIV_LVL_M);

  // Request to take interrupt if there is a locally enabled interrupt while interrupts are also enabled globally
  assign irq_req_ctrl_o = (|irq_local_qual) && global_irq_enable;

  // Interrupt Encoder
  //
  // - sets correct id to request to ID
  // - encodes priority order

  always_comb
  begin
    if      (irq_local_qual[31]) irq_id_ctrl_o = 5'd31;                         // Custom irq_i[31]
    else if (irq_local_qual[30]) irq_id_ctrl_o = 5'd30;                         // Custom irq_i[30]
    else if (irq_local_qual[29]) irq_id_ctrl_o = 5'd29;                         // Custom irq_i[29]
    else if (irq_local_qual[28]) irq_id_ctrl_o = 5'd28;                         // Custom irq_i[28]
    else if (irq_local_qual[27]) irq_id_ctrl_o = 5'd27;                         // Custom irq_i[27]
    else if (irq_local_qual[26]) irq_id_ctrl_o = 5'd26;                         // Custom irq_i[26]
    else if (irq_local_qual[25]) irq_id_ctrl_o = 5'd25;                         // Custom irq_i[25]
    else if (irq_local_qual[24]) irq_id_ctrl_o = 5'd24;                         // Custom irq_i[24]
    else if (irq_local_qual[23]) irq_id_ctrl_o = 5'd23;                         // Custom irq_i[23]
    else if (irq_local_qual[22]) irq_id_ctrl_o = 5'd22;                         // Custom irq_i[22]
    else if (irq_local_qual[21]) irq_id_ctrl_o = 5'd21;                         // Custom irq_i[21]
    else if (irq_local_qual[20]) irq_id_ctrl_o = 5'd20;                         // Custom irq_i[20]
    else if (irq_local_qual[19]) irq_id_ctrl_o = 5'd19;                         // Custom irq_i[19]
    else if (irq_local_qual[18]) irq_id_ctrl_o = 5'd18;                         // Custom irq_i[18]
    else if (irq_local_qual[17]) irq_id_ctrl_o = 5'd17;                         // Custom irq_i[17]
    else if (irq_local_qual[16]) irq_id_ctrl_o = 5'd16;                         // Custom irq_i[16]

    // Reserved: irq_local_qual[15], irq_id_ctrl_o = 5'd15
    // Reserved: irq_local_qual[14], irq_id_ctrl_o = 5'd14
    // Reserved: irq_local_qual[13], irq_id_ctrl_o = 5'd13
    // Reserved: irq_local_qual[12], irq_id_ctrl_o = 5'd12

    else if (irq_local_qual[CSR_MEIX_BIT]) irq_id_ctrl_o = CSR_MEIX_BIT;        // MEI, irq_i[11]
    else if (irq_local_qual[CSR_MSIX_BIT]) irq_id_ctrl_o = CSR_MSIX_BIT;        // MSI, irq_i[3]
    else                                   irq_id_ctrl_o = CSR_MTIX_BIT;        // MTI, irq_i[7]

    // Reserved: irq_local_qual[10], irq_id_ctrl_o = 5'd10
    // Reserved: irq_local_qual[ 2], irq_id_ctrl_o = 5'd2
    // Reserved: irq_local_qual[ 6], irq_id_ctrl_o = 5'd6

    // Reserved: irq_local_qual[ 9], irq_id_ctrl_o = 5'd9, SEI
    // Reserved: irq_local_qual[ 1], irq_id_ctrl_o = 5'd1, SSI
    // Reserved: irq_local_qual[ 5], irq_id_ctrl_o = 5'd5, STI

    // Reserved: irq_local_qual[ 8], irq_id_ctrl_o = 5'd8, UEI
    // Reserved: irq_local_qual[ 0], irq_id_ctrl_o = 5'd0, USI
    // Reserved: irq_local_qual[ 4], irq_id_ctrl_o = 5'd4, UTI

  end

endmodule
