../src/lowrisc_ip_rv_plic_component_0.1/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: `include "prim_assert.sv"
  16: 
  17: module rv_plic_target #(
  18:   parameter int N_SOURCE = 32,
  19:   parameter int MAX_PRIO = 7,
  20: 
  21:   // Local param (Do not change this through parameter
  22:   localparam int SrcWidth  = $clog2(N_SOURCE+1),  // derived parameter
  23:   localparam int PrioWidth = $clog2(MAX_PRIO+1)   // derived parameter
  24: ) (
  25:   input clk_i,
  26:   input rst_ni,
  27: 
  28:   input [N_SOURCE-1:0]  ip,
  29:   input [N_SOURCE-1:0]  ie,
  30: 
  31:   input [PrioWidth-1:0] prio [N_SOURCE],
  32:   input [PrioWidth-1:0] threshold,
  33: 
  34:   output logic            irq,
  35:   output logic [SrcWidth-1:0] irq_id
  36: );
  37: 
  38:   // this only works with 2 or more sources
  39:   `ASSERT_INIT(NumSources_A, N_SOURCE >= 2)
  40: 
  41:   // align to powers of 2 for simplicity
  42:   // a full binary tree with N levels has 2**N + 2**N-1 nodes
  43:   localparam int NumLevels = $clog2(N_SOURCE);
  44:   logic [2**(NumLevels+1)-2:0]            is_tree;
  45:   logic [2**(NumLevels+1)-2:0][SrcWidth-1:0]  id_tree;
  46:   logic [2**(NumLevels+1)-2:0][PrioWidth-1:0] max_tree;
  47: 
  48:   for (genvar level = 0; level < NumLevels+1; level++) begin : gen_tree
  49:     //
  50:     // level+1   C0   C1   <- "Base1" points to the first node on "level+1",
  51:     //            \  /         these nodes are the children of the nodes one level below
  52:     // level       Pa      <- "Base0", points to the first node on "level",
  53:     //                         these nodes are the parents of the nodes one level above
  54:     //
  55:     // hence we have the following indices for the paPa, C0, C1 nodes:
  56:     // Pa = 2**level     - 1 + offset       = Base0 + offset
  57:     // C0 = 2**(level+1) - 1 + 2*offset     = Base1 + 2*offset
  58:     // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1
  59:     //
  60:     localparam int Base0 = (2**level)-1;
  61:     localparam int Base1 = (2**(level+1))-1;
  62: 
  63:     for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level
  64:       localparam int Pa = Base0 + offset;
  65:       localparam int C0 = Base1 + 2*offset;
  66:       localparam int C1 = Base1 + 2*offset + 1;
  67: 
  68:       // this assigns the gated interrupt source signals, their
  69:       // corresponding IDs and priorities to the tree leafs
  70:       if (level == NumLevels) begin : gen_leafs
  71:         if (offset < N_SOURCE) begin : gen_assign
  72:           assign is_tree[Pa]  = ip[offset] & ie[offset];
  73:           assign id_tree[Pa]  = offset;
  74:           assign max_tree[Pa] = prio[offset];
  75:         end else begin : gen_tie_off
  76:           assign is_tree[Pa]  = '0;
  77:           assign id_tree[Pa]  = '0;
  78:           assign max_tree[Pa] = '0;
  79:         end
  80:       // this creates the node assignments
  81:       end else begin : gen_nodes
  82:         // NOTE: the code below has been written in this way in order to work
  83:         // around a synthesis issue in Vivado 2018.3 and 2019.2 where the whole
  84:         // module would be optimized away if these assign statements contained
  85:         // ternary statements to implement the muxes.
  86:         //
  87:         // TODO: rewrite these lines with ternary statmements onec the problem
  88:         // has been fixed in the tool.
  89:         //
  90:         // See also originating issue:
  91:         // https://github.com/lowRISC/opentitan/issues/1355
  92:         // Xilinx issue:
  93:         // https://forums.xilinx.com/t5/Synthesis/Simulation-Synthesis-Mismatch-with-Vivado-2018-3/m-p/1065923#M33849
  94: 
  95:         logic sel; // local helper variable
  96:         // in case only one of the parent has a pending irq, forward that one
  97:         // in case both irqs are pending, forward the one with higher priority
  98:         assign sel = (~is_tree[C0] & is_tree[C1]) |
  99:                      (is_tree[C0] & is_tree[C1] & logic'(max_tree[C1] > max_tree[C0]));
 100:         // forwarding muxes
 101:         assign is_tree[Pa]  = (sel              & is_tree[C1])  | ((~sel)            & is_tree[C0]);
 102:         assign id_tree[Pa]  = ({SrcWidth{sel}}  & id_tree[C1])  | ({SrcWidth{~sel}}  & id_tree[C0]);
 103:         assign max_tree[Pa] = ({PrioWidth{sel}} & max_tree[C1]) | ({PrioWidth{~sel}} & max_tree[C0]);
 104:       end
 105:     end : gen_level
 106:   end : gen_tree
 107: 
 108:   logic irq_d, irq_q;
 109:   logic [SrcWidth-1:0] irq_id_d, irq_id_q;
 110: 
 111:   // the results can be found at the tree root
 112:   assign irq_d    = (max_tree[0] > threshold) ? is_tree[0] : 1'b0;
 113:   assign irq_id_d = (is_tree[0]) ? id_tree[0] : '0;
 114: 
 115:   always_ff @(posedge clk_i or negedge rst_ni) begin : gen_regs
 116:     if (!rst_ni) begin
 117:       irq_q    <= 1'b0;
 118:       irq_id_q <= '0;
 119:     end else begin
 120:       irq_q    <= irq_d;
 121:       irq_id_q <= irq_id_d;
 122:     end
 123:   end
 124: 
 125:   assign irq    = irq_q;
 126:   assign irq_id = irq_id_q;
 127: 
 128: endmodule
 129: 
 130: