// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// RISC-V Platform-Level Interrupt Controller compliant INTC
//
//   Current version doesn't support MSI interrupt but it is easy to add
//   the feature. Create one external register and connect qe signal to the
//   gateway module (as edge-triggered)
//
//   Consider to set MAX_PRIO as small number as possible. It is main factor
//   of area increase if edge-triggered counter isn't implemented.
//
// verilog parameter
//   N_SOURCE: Number of interrupt sources
//   N_TARGET: Number of interrupt targets (#receptor)
//   MAX_PRIO: Maximum value of interrupt priority
//   SRCW:     Do not specify this parameter. It is bit width to cover MAX_ID+1

module rv_plic #(
  parameter int N_SOURCE    = 32,
  parameter int N_TARGET    = 1,
  parameter int MAX_PRIO    = 7,
  parameter     FIND_MAX    = "SEQUENTIAL", // SEQUENTIAL | MATRIX

  // Local param (Do not change this through parameter)
  parameter int SRCW        = $clog2(N_SOURCE+1)
) (
  input     clk_i,
  input     rst_ni,

  // Bus Interface (device)
  input  tlul_pkg::tl_h2d_t tl_i,
  output tlul_pkg::tl_d2h_t tl_o,

  // Interrupt Sources
  input [N_SOURCE-1:0] intr_src_i,

  // Interrupt notification to targets
  output            irq_o    [N_TARGET],
  output [SRCW-1:0] irq_id_o [N_TARGET]
);

import rv_plic_reg_pkg::*;

rv_plic_reg2hw_t reg2hw;
rv_plic_hw2reg_t hw2reg;

localparam PRIOW = $clog2(MAX_PRIO+1);

logic [N_SOURCE-1:0] le; // 0:level 1:edge
logic [N_SOURCE-1:0] ip;

logic [N_SOURCE-1:0] ie [N_TARGET];

logic                claim_re [N_TARGET]; // Target read indicator
logic [SRCW-1:0]     claim_id [N_TARGET];
logic [N_SOURCE-1:0] claim ; // Converted from claim_re/claim_id

logic                complete_we [N_TARGET]; // Target write indicator
logic [SRCW-1:0]     complete_id [N_TARGET];
logic [N_SOURCE-1:0] complete ; // Converted from complete_re/complete_id

logic [SRCW-1:0]     cc_id [N_TARGET]; // Write ID

logic [PRIOW-1:0] prio [N_SOURCE];

logic [PRIOW-1:0] threshold [N_TARGET];

// Glue logic between rv_plic_reg_top and others
assign cc_id = irq_id_o;

always_comb begin
  claim = '0;
  for (int i = 0 ; i < N_TARGET ; i++) begin
    if (claim_re[i]) claim[claim_id[i] -1] = 1'b1;
  end
end
always_comb begin
  complete = '0;
  for (int i = 0 ; i < N_TARGET ; i++) begin
    if (complete_we[i]) complete[complete_id[i] -1] = 1'b1;
  end
end

`ifndef VERILATOR
//sequence pulse_seq (sig);
//  sig ##1 ~sig;
//endsequence

property pulse_prop (sig);
  @(posedge clk_i)
    sig |=> ##1 ~sig;
endproperty

//claim_pulse_assert:    assert property (pulse_prop(claim_re[i]));
//complete_pulse_assert: assert property (pulse_prop(complete_we[i]));
onehot0_claim_assert:    assert property (
  @(posedge clk_i) disable iff (!rst_ni)
    $onehot0(claim_re)
);
onehot0_complete_assert: assert property (
  @(posedge clk_i) disable iff (!rst_ni)
    $onehot0(complete_we)
);
`endif // VERILATOR

//pragma translate_off
if(SRCW != $clog2(N_SOURCE+1)) $fatal("SRCW shouldn't be modified");
//pragma translate_on

////////////////////////////////////////////////////////////////////////////////
// PRIORITY : Need to connect manually
assign prio[ 0] = reg2hw.prio0.q;
assign prio[ 1] = reg2hw.prio1.q;
assign prio[ 2] = reg2hw.prio2.q;
assign prio[ 3] = reg2hw.prio3.q;
assign prio[ 4] = reg2hw.prio4.q;
assign prio[ 5] = reg2hw.prio5.q;
assign prio[ 6] = reg2hw.prio6.q;
assign prio[ 7] = reg2hw.prio7.q;
assign prio[ 8] = reg2hw.prio8.q;
assign prio[ 9] = reg2hw.prio9.q;
assign prio[10] = reg2hw.prio10.q;
assign prio[11] = reg2hw.prio11.q;
assign prio[12] = reg2hw.prio12.q;
assign prio[13] = reg2hw.prio13.q;
assign prio[14] = reg2hw.prio14.q;
assign prio[15] = reg2hw.prio15.q;
assign prio[16] = reg2hw.prio16.q;
assign prio[17] = reg2hw.prio17.q;
assign prio[18] = reg2hw.prio18.q;
assign prio[19] = reg2hw.prio19.q;
assign prio[20] = reg2hw.prio20.q;
assign prio[21] = reg2hw.prio21.q;
assign prio[22] = reg2hw.prio22.q;
assign prio[23] = reg2hw.prio23.q;
assign prio[24] = reg2hw.prio24.q;
assign prio[25] = reg2hw.prio25.q;
assign prio[26] = reg2hw.prio26.q;
assign prio[27] = reg2hw.prio27.q;
assign prio[28] = reg2hw.prio28.q;
assign prio[29] = reg2hw.prio29.q;
assign prio[30] = reg2hw.prio30.q;
assign prio[31] = reg2hw.prio31.q;
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
// Interrupt Enable : Need to connect manually
assign ie[0][ 0] = reg2hw.ie0.e0.q;
assign ie[0][ 1] = reg2hw.ie0.e1.q;
assign ie[0][ 2] = reg2hw.ie0.e2.q;
assign ie[0][ 3] = reg2hw.ie0.e3.q;
assign ie[0][ 4] = reg2hw.ie0.e4.q;
assign ie[0][ 5] = reg2hw.ie0.e5.q;
assign ie[0][ 6] = reg2hw.ie0.e6.q;
assign ie[0][ 7] = reg2hw.ie0.e7.q;
assign ie[0][ 8] = reg2hw.ie0.e8.q;
assign ie[0][ 9] = reg2hw.ie0.e9.q;
assign ie[0][10] = reg2hw.ie0.e10.q;
assign ie[0][11] = reg2hw.ie0.e11.q;
assign ie[0][12] = reg2hw.ie0.e12.q;
assign ie[0][13] = reg2hw.ie0.e13.q;
assign ie[0][14] = reg2hw.ie0.e14.q;
assign ie[0][15] = reg2hw.ie0.e15.q;
assign ie[0][16] = reg2hw.ie0.e16.q;
assign ie[0][17] = reg2hw.ie0.e17.q;
assign ie[0][18] = reg2hw.ie0.e18.q;
assign ie[0][19] = reg2hw.ie0.e19.q;
assign ie[0][20] = reg2hw.ie0.e20.q;
assign ie[0][21] = reg2hw.ie0.e21.q;
assign ie[0][22] = reg2hw.ie0.e22.q;
assign ie[0][23] = reg2hw.ie0.e23.q;
assign ie[0][24] = reg2hw.ie0.e24.q;
assign ie[0][25] = reg2hw.ie0.e25.q;
assign ie[0][26] = reg2hw.ie0.e26.q;
assign ie[0][27] = reg2hw.ie0.e27.q;
assign ie[0][28] = reg2hw.ie0.e28.q;
assign ie[0][29] = reg2hw.ie0.e29.q;
assign ie[0][30] = reg2hw.ie0.e30.q;
assign ie[0][31] = reg2hw.ie0.e31.q;
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
// THRESHOLD : Need to connect manually
assign threshold[0] = reg2hw.threshold0.q;
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
// CC : Need to connect manually
assign claim_re[0] = reg2hw.cc0.re;
assign claim_id[0] = irq_id_o[0];
assign complete_we[0] = reg2hw.cc0.qe;
assign complete_id[0] = reg2hw.cc0.q;

assign hw2reg.cc0.d  = cc_id[0];
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
// IP : Need to connect manually
assign hw2reg.ip.p0.de = 1'b1; // Always write
assign hw2reg.ip.p1.de = 1'b1; // Always write
assign hw2reg.ip.p2.de = 1'b1; // Always write
assign hw2reg.ip.p3.de = 1'b1; // Always write
assign hw2reg.ip.p4.de = 1'b1; // Always write
assign hw2reg.ip.p5.de = 1'b1; // Always write
assign hw2reg.ip.p6.de = 1'b1; // Always write
assign hw2reg.ip.p7.de = 1'b1; // Always write
assign hw2reg.ip.p8.de = 1'b1; // Always write
assign hw2reg.ip.p9.de = 1'b1; // Always write
assign hw2reg.ip.p10.de = 1'b1; // Always write
assign hw2reg.ip.p11.de = 1'b1; // Always write
assign hw2reg.ip.p12.de = 1'b1; // Always write
assign hw2reg.ip.p13.de = 1'b1; // Always write
assign hw2reg.ip.p14.de = 1'b1; // Always write
assign hw2reg.ip.p15.de = 1'b1; // Always write
assign hw2reg.ip.p16.de = 1'b1; // Always write
assign hw2reg.ip.p17.de = 1'b1; // Always write
assign hw2reg.ip.p18.de = 1'b1; // Always write
assign hw2reg.ip.p19.de = 1'b1; // Always write
assign hw2reg.ip.p20.de = 1'b1; // Always write
assign hw2reg.ip.p21.de = 1'b1; // Always write
assign hw2reg.ip.p22.de = 1'b1; // Always write
assign hw2reg.ip.p23.de = 1'b1; // Always write
assign hw2reg.ip.p24.de = 1'b1; // Always write
assign hw2reg.ip.p25.de = 1'b1; // Always write
assign hw2reg.ip.p26.de = 1'b1; // Always write
assign hw2reg.ip.p27.de = 1'b1; // Always write
assign hw2reg.ip.p28.de = 1'b1; // Always write
assign hw2reg.ip.p29.de = 1'b1; // Always write
assign hw2reg.ip.p30.de = 1'b1; // Always write
assign hw2reg.ip.p31.de = 1'b1; // Always write
assign hw2reg.ip.p0.d  = ip[0];
assign hw2reg.ip.p1.d  = ip[1];
assign hw2reg.ip.p2.d  = ip[2];
assign hw2reg.ip.p3.d  = ip[3];
assign hw2reg.ip.p4.d  = ip[4];
assign hw2reg.ip.p5.d  = ip[5];
assign hw2reg.ip.p6.d  = ip[6];
assign hw2reg.ip.p7.d  = ip[7];
assign hw2reg.ip.p8.d  = ip[8];
assign hw2reg.ip.p9.d  = ip[9];
assign hw2reg.ip.p10.d  = ip[10];
assign hw2reg.ip.p11.d  = ip[11];
assign hw2reg.ip.p12.d  = ip[12];
assign hw2reg.ip.p13.d  = ip[13];
assign hw2reg.ip.p14.d  = ip[14];
assign hw2reg.ip.p15.d  = ip[15];
assign hw2reg.ip.p16.d  = ip[16];
assign hw2reg.ip.p17.d  = ip[17];
assign hw2reg.ip.p18.d  = ip[18];
assign hw2reg.ip.p19.d  = ip[19];
assign hw2reg.ip.p20.d  = ip[20];
assign hw2reg.ip.p21.d  = ip[21];
assign hw2reg.ip.p22.d  = ip[22];
assign hw2reg.ip.p23.d  = ip[23];
assign hw2reg.ip.p24.d  = ip[24];
assign hw2reg.ip.p25.d  = ip[25];
assign hw2reg.ip.p26.d  = ip[26];
assign hw2reg.ip.p27.d  = ip[27];
assign hw2reg.ip.p28.d  = ip[28];
assign hw2reg.ip.p29.d  = ip[29];
assign hw2reg.ip.p30.d  = ip[30];
assign hw2reg.ip.p31.d  = ip[31];
//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
// LE : Need to connect manually
assign le[0] = reg2hw.le.le0.q;
assign le[1] = reg2hw.le.le1.q;
assign le[2] = reg2hw.le.le2.q;
assign le[3] = reg2hw.le.le3.q;
assign le[4] = reg2hw.le.le4.q;
assign le[5] = reg2hw.le.le5.q;
assign le[6] = reg2hw.le.le6.q;
assign le[7] = reg2hw.le.le7.q;
assign le[8] = reg2hw.le.le8.q;
assign le[9] = reg2hw.le.le9.q;
assign le[10] = reg2hw.le.le10.q;
assign le[11] = reg2hw.le.le11.q;
assign le[12] = reg2hw.le.le12.q;
assign le[13] = reg2hw.le.le13.q;
assign le[14] = reg2hw.le.le14.q;
assign le[15] = reg2hw.le.le15.q;
assign le[16] = reg2hw.le.le16.q;
assign le[17] = reg2hw.le.le17.q;
assign le[18] = reg2hw.le.le18.q;
assign le[19] = reg2hw.le.le19.q;
assign le[20] = reg2hw.le.le20.q;
assign le[21] = reg2hw.le.le21.q;
assign le[22] = reg2hw.le.le22.q;
assign le[23] = reg2hw.le.le23.q;
assign le[24] = reg2hw.le.le24.q;
assign le[25] = reg2hw.le.le25.q;
assign le[26] = reg2hw.le.le26.q;
assign le[27] = reg2hw.le.le27.q;
assign le[28] = reg2hw.le.le28.q;
assign le[29] = reg2hw.le.le29.q;
assign le[30] = reg2hw.le.le30.q;
assign le[31] = reg2hw.le.le31.q;
//------------------------------------------------------------------------------

// Gateways
rv_plic_gateway #(
  .N_SOURCE (N_SOURCE)
) u_gateway (
  .clk_i,
  .rst_ni,

  .src (intr_src_i),
  .le,

  .claim,
  .complete,

  .ip
);


// Target interrupt notification
for (genvar i = 0 ; i < N_TARGET ; i++) begin : gen_target
  rv_plic_target #(
    .N_SOURCE (N_SOURCE),
    .MAX_PRIO (MAX_PRIO),
    .ALGORITHM(FIND_MAX)
  ) u_target (
    .clk_i,
    .rst_ni,

    .ip,
    .ie        (ie[i]),

    .prio,
    .threshold (threshold[i]),

    .irq       (irq_o[i]),
    .irq_id    (irq_id_o[i])

  );
end

// Register interface
//  Limitation of register tool prevents the module from having flexibility to parameters
//  So, signals are manually tied at the top.
rv_plic_reg_top u_reg (
  .clk_i,
  .rst_ni,

  .tl_i,
  .tl_o,

  .reg2hw,
  .hw2reg
);

endmodule

