blob: f668469f54b6e502e710f4ebd4c276246e5f447b [file] [log] [blame]
// 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
// MAX_PRIO: Maximum value of interrupt priority
module rv_plic_smc import rv_plic_smc_reg_pkg::*; #(
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
// OpenTitan IP standardizes on level triggered interrupts,
// hence LevelEdgeTrig is set to all-zeroes by default.
// Note that in case of edge-triggered interrupts, CDC handling is not
// fully implemented yet (this would require instantiating pulse syncs
// and routing the source clocks / resets to the PLIC).
parameter logic [NumSrc-1:0] LevelEdgeTrig = '0, // 0: level, 1: edge
// derived parameter
localparam int SRCW = $clog2(NumSrc)
) (
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 [NumSrc-1:0] intr_src_i,
// Alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
// Interrupt notification to targets
output [NumTarget-1:0] irq_o,
output [SRCW-1:0] irq_id_o [NumTarget],
output logic [NumTarget-1:0] msip_o
);
rv_plic_smc_reg2hw_t reg2hw;
rv_plic_smc_hw2reg_t hw2reg;
localparam int MAX_PRIO = 3;
localparam int PRIOW = $clog2(MAX_PRIO+1);
logic [NumSrc-1:0] ip;
logic [NumSrc-1:0] ie [NumTarget];
logic [NumTarget-1:0] claim_re; // Target read indicator
logic [SRCW-1:0] claim_id [NumTarget];
logic [NumSrc-1:0] claim; // Converted from claim_re/claim_id
logic [NumTarget-1:0] complete_we; // Target write indicator
logic [SRCW-1:0] complete_id [NumTarget];
logic [NumSrc-1:0] complete; // Converted from complete_re/complete_id
logic [SRCW-1:0] cc_id [NumTarget]; // Write ID
logic [NumSrc-1:0][PRIOW-1:0] prio;
logic [PRIOW-1:0] threshold [NumTarget];
// Glue logic between rv_plic_smc_reg_top and others
assign cc_id = irq_id_o;
always_comb begin
claim = '0;
for (int i = 0 ; i < NumTarget ; i++) begin
if (claim_re[i]) claim[claim_id[i]] = 1'b1;
end
end
always_comb begin
complete = '0;
for (int i = 0 ; i < NumTarget ; i++) begin
if (complete_we[i]) complete[complete_id[i]] = 1'b1;
end
end
//`ASSERT_PULSE(claimPulse, claim_re[i])
//`ASSERT_PULSE(completePulse, complete_we[i])
`ASSERT(onehot0Claim, $onehot0(claim_re))
`ASSERT(onehot0Complete, $onehot0(complete_we))
//////////////
// Priority //
//////////////
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;
assign prio[32] = reg2hw.prio32.q;
assign prio[33] = reg2hw.prio33.q;
assign prio[34] = reg2hw.prio34.q;
assign prio[35] = reg2hw.prio35.q;
assign prio[36] = reg2hw.prio36.q;
assign prio[37] = reg2hw.prio37.q;
assign prio[38] = reg2hw.prio38.q;
assign prio[39] = reg2hw.prio39.q;
assign prio[40] = reg2hw.prio40.q;
assign prio[41] = reg2hw.prio41.q;
assign prio[42] = reg2hw.prio42.q;
//////////////////////
// Interrupt Enable //
//////////////////////
for (genvar s = 0; s < 43; s++) begin : gen_ie0
assign ie[0][s] = reg2hw.ie0[s].q;
end
////////////////////////
// THRESHOLD register //
////////////////////////
assign threshold[0] = reg2hw.threshold0.q;
/////////////////
// CC register //
/////////////////
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];
///////////////////
// MSIP register //
///////////////////
assign msip_o[0] = reg2hw.msip0.q;
////////
// IP //
////////
for (genvar s = 0; s < 43; s++) begin : gen_ip
assign hw2reg.ip[s].de = 1'b1; // Always write
assign hw2reg.ip[s].d = ip[s];
end
//////////////
// Gateways //
//////////////
// Synchronize all incoming interrupt requests.
logic [NumSrc-1:0] intr_src_synced;
prim_flop_2sync #(
.Width(NumSrc)
) u_prim_flop_2sync (
.clk_i,
.rst_ni,
.d_i(intr_src_i),
.q_o(intr_src_synced)
);
rv_plic_smc_gateway #(
.N_SOURCE (NumSrc)
) u_gateway (
.clk_i,
.rst_ni,
.src_i (intr_src_synced),
.le_i (LevelEdgeTrig),
.claim_i (claim),
.complete_i (complete),
.ip_o (ip)
);
///////////////////////////////////
// Target interrupt notification //
///////////////////////////////////
for (genvar i = 0 ; i < NumTarget ; i++) begin : gen_target
rv_plic_smc_target #(
.N_SOURCE (NumSrc),
.MAX_PRIO (MAX_PRIO)
) u_target (
.clk_i,
.rst_ni,
.ip_i (ip),
.ie_i (ie[i]),
.prio_i (prio),
.threshold_i (threshold[i]),
.irq_o (irq_o[i]),
.irq_id_o (irq_id_o[i])
);
end
////////////
// Alerts //
////////////
logic [NumAlerts-1:0] alert_test, alerts;
assign alert_test = {
reg2hw.alert_test.q &
reg2hw.alert_test.qe
};
for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[i]),
.IsFatal(1'b1)
) u_prim_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test[i] ),
.alert_req_i ( alerts[i] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_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_smc_reg_top u_reg (
.clk_i,
.rst_ni,
.tl_i,
.tl_o,
.reg2hw,
.hw2reg,
// SEC_CM: BUS.INTEGRITY
.intg_err_o(alerts[0]),
.devmode_i (1'b1)
);
// Assertions
`ASSERT_KNOWN(TlDValidKnownO_A, tl_o.d_valid)
`ASSERT_KNOWN(TlAReadyKnownO_A, tl_o.a_ready)
`ASSERT_KNOWN(IrqKnownO_A, irq_o)
`ASSERT_KNOWN(MsipKnownO_A, msip_o)
for (genvar k = 0; k < NumTarget; k++) begin : gen_irq_id_known
`ASSERT_KNOWN(IrqIdKnownO_A, irq_id_o[k])
end
// Assume
`ASSUME(Irq0Tied_A, intr_src_i[0] == 1'b0)
// Alert assertions for reg_we onehot check
`ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0])
endmodule