hw/ip/rv_plic/rtl/rv_plic_target.sv Cov: 89.1%
1: // Copyright lowRISC contributors.
2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3: // SPDX-License-Identifier: Apache-2.0
4: //
5: // RISC-V Platform-Level Interrupt Generator for Target
6: //
7: // This module basically doing IE & IP based on priority and threshold.
8: // Keep in mind that increasing MAX_PRIO affects logic size a lot.
9: //
10: // The module implements a binary tree to find the maximal entry. the solution
11: // has O(N) area and O(log(N)) delay complexity, and thus scales well with
12: // many input sources.
13: //
14:
15: module rv_plic_target #(
16: parameter int N_SOURCE = 32,
17: parameter int MAX_PRIO = 7,
18:
19: // Local param (Do not change this through parameter
20: localparam int unsigned SRCW = $clog2(N_SOURCE+1), // derived parameter
21: localparam int unsigned PRIOW = $clog2(MAX_PRIO+1) // derived parameter
22: ) (
23: input clk_i,
24: input rst_ni,
25:
26: input [N_SOURCE-1:0] ip,
27: input [N_SOURCE-1:0] ie,
28:
29: input [PRIOW-1:0] prio [N_SOURCE],
30: input [PRIOW-1:0] threshold,
31:
32: output logic irq,
33: output logic [SRCW-1:0] irq_id
34: );
35:
36: // this only works with 2 or more sources
37: `ASSERT_INIT(NumSources_A, N_SOURCE >= 2)
38:
39: // align to powers of 2 for simplicity
40: // a full binary tree with N levels has 2**N + 2**N-1 nodes
41: localparam int unsigned N_LEVELS = $clog2(N_SOURCE);
42: logic [2**(N_LEVELS+1)-2:0] is_tree;
43: logic [2**(N_LEVELS+1)-2:0][SRCW-1:0] id_tree;
44: logic [2**(N_LEVELS+1)-2:0][PRIOW-1:0] max_tree;
45:
46: for (genvar level = 0; level < N_LEVELS+1; level++) begin : gen_tree
47: //
48: // level+1 c0 c1 <- "base1" points to the first node on "level+1",
49: // \ / these nodes are the children of the nodes one level below
50: // level pa <- "base0", points to the first node on "level",
51: // these nodes are the parents of the nodes one level above
52: //
53: // hence we have the following indices for the pa, c0, c1 nodes:
54: // pa = 2**level - 1 + offset = base0 + offset
55: // c0 = 2**(level+1) - 1 + 2*offset = base1 + 2*offset
56: // c1 = 2**(level+1) - 1 + 2*offset + 1 = base1 + 2*offset + 1
57: //
58: localparam int unsigned base0 = (2**level)-1;
59: localparam int unsigned base1 = (2**(level+1))-1;
60:
61: for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level
62: localparam int unsigned pa = base0 + offset;
63: localparam int unsigned c0 = base1 + 2*offset;
64: localparam int unsigned c1 = base1 + 2*offset + 1;
65:
66: // this assigns the gated interrupt source signals, their
67: // corresponding IDs and priorities to the tree leafs
68: if (level == N_LEVELS) begin : gen_leafs
69: if (offset < N_SOURCE) begin : gen_assign
70: assign is_tree[pa] = ip[offset] & ie[offset];
71: assign id_tree[pa] = offset+1'b1;
72: assign max_tree[pa] = prio[offset];
73: end else begin : gen_tie_off
74: assign is_tree[pa] = '0;
75: assign id_tree[pa] = '0;
76: assign max_tree[pa] = '0;
77: end
78: // this creates the node assignments
79: end else begin : gen_nodes
80: // NOTE: the code below has been written in this way in order to work
81: // around a synthesis issue in Vivado 2018.3 and 2019.2 where the whole
82: // module would be optimized away if these assign statements contained
83: // ternary statements to implement the muxes.
84: //
85: // TODO: rewrite these lines with ternary statmements onec the problem
86: // has been fixed in the tool.
87: //
88: // See also originating issue:
89: // https://github.com/lowRISC/opentitan/issues/1355
90: // Xilinx issue:
91: // https://forums.xilinx.com/t5/Synthesis/Simulation-Synthesis-Mismatch-with-Vivado-2018-3/m-p/1065923#M33849
92:
93: logic sel; // local helper variable
94: // in case only one of the parent has a pending irq, forward that one
95: // in case both irqs are pending, forward the one with higher priority
96: assign sel = (~is_tree[c0] & is_tree[c1]) |
97: (is_tree[c0] & is_tree[c1] & logic'(max_tree[c1] > max_tree[c0]));
98: // forwarding muxes
99: assign is_tree[pa] = (sel & is_tree[c1]) | ((~sel) & is_tree[c0]);
100: assign id_tree[pa] = ({SRCW{sel}} & id_tree[c1]) | ({SRCW{~sel}} & id_tree[c0]);
101: assign max_tree[pa] = ({PRIOW{sel}} & max_tree[c1]) | ({PRIOW{~sel}} & max_tree[c0]);
102: end
103: end : gen_level
104: end : gen_tree
105:
106: logic irq_d, irq_q;
107: logic [SRCW-1:0] irq_id_d, irq_id_q;
108:
109: // the results can be found at the tree root
110: assign irq_d = (max_tree[0] > threshold) ? is_tree[0] : 1'b0;
111: assign irq_id_d = (is_tree[0]) ? id_tree[0] : '0;
112:
113: always_ff @(posedge clk_i or negedge rst_ni) begin : gen_regs
114: if (!rst_ni) begin
115: irq_q <= 1'b0;
116: irq_id_q <= '0;
117: end else begin
118: irq_q <= irq_d;
119: irq_id_q <= irq_id_d;
120: end
121: end
122:
123: assign irq = irq_q;
124: assign irq_id = irq_id_q;
125:
126: endmodule
127:
128: