blob: 1813194e114e7ca88f8b5f98c570e269035e6247 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Primitive interrupt handler. This assumes the existence of three
// controller registers: INTR_ENABLE, INTR_STATE, INTR_TEST.
// This module can be instantiated once per interrupt field, or
// "bussified" with all fields of the interrupt vector.
module prim_intr_hw # (
parameter int unsigned Width = 1,
parameter bit FlopOutput = 1,
// IntrT parameter is to hint the logic for the interrupt type. Module
// supports two interrupt types, *Status* and *Event*.
//
// The differences between those two types are:
// - Status is persistent. Until the root cause is alleviated, the interrupt
// keeps asserting.
// - Event remains high for a relatively short period time without SW
// intervention. One distinct example is an error (error could be status
// though). If a certain error condition is captured, HW logic may create a
// pulse. In this case the interrupt is assumed as an Event interrupt.
parameter IntrT = "Event" // Event or Status
) (
// event
input clk_i,
input rst_ni,
input [Width-1:0] event_intr_i,
// register interface
input [Width-1:0] reg2hw_intr_enable_q_i,
input [Width-1:0] reg2hw_intr_test_q_i,
input reg2hw_intr_test_qe_i,
input [Width-1:0] reg2hw_intr_state_q_i,
output hw2reg_intr_state_de_o,
output [Width-1:0] hw2reg_intr_state_d_o,
// outgoing interrupt
output logic [Width-1:0] intr_o
);
logic [Width-1:0] status; // incl. test
if (IntrT == "Event") begin : g_intr_event
logic [Width-1:0] new_event;
assign new_event =
(({Width{reg2hw_intr_test_qe_i}} & reg2hw_intr_test_q_i) | event_intr_i);
assign hw2reg_intr_state_de_o = |new_event;
// for scalar interrupts, this resolves to '1' with new event
// for vector interrupts, new events are OR'd in to existing interrupt state
assign hw2reg_intr_state_d_o = new_event | reg2hw_intr_state_q_i;
assign status = reg2hw_intr_state_q_i ;
end : g_intr_event
else if (IntrT == "Status") begin : g_intr_status
logic [Width-1:0] test_q; // Storing test. Cleared by SW
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) test_q <= '0;
else if (reg2hw_intr_test_qe_i) test_q <= reg2hw_intr_test_q_i;
end
// TODO: In Status type, INTR_STATE is better to be external type and RO.
assign hw2reg_intr_state_de_o = 1'b 1; // always represent the status
assign hw2reg_intr_state_d_o = event_intr_i | test_q;
assign status = event_intr_i | test_q;
// To make the timing same to event type, status signal does not use CSR.q,
// rather the input of the CSR.
logic unused_reg2hw;
assign unused_reg2hw = ^reg2hw_intr_state_q_i;
end : g_intr_status
if (FlopOutput == 1) begin : gen_flop_intr_output
// flop the interrupt output
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
intr_o <= '0;
end else begin
intr_o <= status & reg2hw_intr_enable_q_i;
end
end
end else begin : gen_intr_passthrough_output
logic unused_clk;
logic unused_rst_n;
assign unused_clk = clk_i;
assign unused_rst_n = rst_ni;
assign intr_o = reg2hw_intr_state_q_i & reg2hw_intr_enable_q_i;
end
`ASSERT_INIT(IntrTKind_A, IntrT inside {"Event", "Status"})
endmodule