blob: d9677dd24b20371b26b8efab6215508e8a398a56 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// N:1 fixed priority arbiter module (index 0 has highest prio)
//
// Verilog parameter
// N: Number of request ports
// DW: Data width
// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored.
//
// See also: prim_arbiter_ppc, prim_arbiter_tree
`include "prim_assert.sv"
module prim_arbiter_fixed #(
parameter int N = 8,
parameter int DW = 32,
// Configurations
// EnDataPort: {0, 1}, if 0, input data will be ignored
parameter bit EnDataPort = 1,
// Derived parameters
localparam int IdxW = $clog2(N)
) (
// used for assertions only
input clk_i,
input rst_ni,
input [ N-1:0] req_i,
input [DW-1:0] data_i [N],
output logic [ N-1:0] gnt_o,
output logic [IdxW-1:0] idx_o,
output logic valid_o,
output logic [DW-1:0] data_o,
input ready_i
);
`ASSERT_INIT(CheckNGreaterZero_A, N > 0)
// this case is basically just a bypass
if (N == 1) begin : gen_degenerate_case
assign valid_o = req_i[0];
assign data_o = data_i[0];
assign gnt_o[0] = valid_o & ready_i;
assign idx_o = '0;
end else begin : gen_normal_case
// align to powers of 2 for simplicity
// a full binary tree with N levels has 2**N + 2**N-1 nodes
logic [2**(IdxW+1)-2:0] req_tree;
logic [2**(IdxW+1)-2:0] gnt_tree;
logic [2**(IdxW+1)-2:0][IdxW-1:0] idx_tree;
logic [2**(IdxW+1)-2:0][DW-1:0] data_tree;
for (genvar level = 0; level < IdxW+1; level++) begin : gen_tree
//
// level+1 C0 C1 <- "Base1" points to the first node on "level+1",
// \ / these nodes are the children of the nodes one level below
// level Pa <- "Base0", points to the first node on "level",
// these nodes are the parents of the nodes one level above
//
// hence we have the following indices for the Pa, C0, C1 nodes:
// Pa = 2**level - 1 + offset = Base0 + offset
// C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset
// C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1
//
localparam int Base0 = (2**level)-1;
localparam int Base1 = (2**(level+1))-1;
for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level
localparam int Pa = Base0 + offset;
localparam int C0 = Base1 + 2*offset;
localparam int C1 = Base1 + 2*offset + 1;
// this assigns the gated interrupt source signals, their
// corresponding IDs and priorities to the tree leafs
if (level == IdxW) begin : gen_leafs
if (offset < N) begin : gen_assign
// forward path
assign req_tree[Pa] = req_i[offset];
assign idx_tree[Pa] = offset;
assign data_tree[Pa] = data_i[offset];
// backward (grant) path
assign gnt_o[offset] = gnt_tree[Pa];
end else begin : gen_tie_off
// forward path
assign req_tree[Pa] = '0;
assign idx_tree[Pa] = '0;
assign data_tree[Pa] = '0;
logic unused_sigs;
assign unused_sigs = gnt_tree[Pa];
end
// this creates the node assignments
end else begin : gen_nodes
// forward path
logic sel; // local helper variable
always_comb begin : p_node
// this always gives priority to the left child
sel = ~req_tree[C0];
// propagate requests
req_tree[Pa] = req_tree[C0] | req_tree[C1];
// data and index muxes
idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0];
data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0];
// propagate the grants back to the input
gnt_tree[C0] = gnt_tree[Pa] & ~sel;
gnt_tree[C1] = gnt_tree[Pa] & sel;
end
end
end : gen_level
end : gen_tree
// the results can be found at the tree root
if (EnDataPort) begin : gen_data_port
assign data_o = data_tree[0];
end else begin : gen_no_dataport
logic [DW-1:0] unused_data;
assign unused_data = data_tree[0];
assign data_o = '1;
end
assign idx_o = idx_tree[0];
assign valid_o = req_tree[0];
// this propagates a grant back to the input
assign gnt_tree[0] = valid_o & ready_i;
end
////////////////
// assertions //
////////////////
// KNOWN assertions on outputs, except for data as that may be partially X in simulation
// e.g. when used on a BUS
`ASSERT_KNOWN(ValidKnown_A, valid_o)
`ASSERT_KNOWN(GrantKnown_A, gnt_o)
`ASSERT_KNOWN(IdxKnown_A, idx_o)
// Make sure no higher prio req is asserted
`ASSERT(Priority_A, |req_i |-> req_i[idx_o] && (((N'(1'b1) << idx_o) - 1'b1) & req_i) == '0)
// we can only grant one requestor at a time
`ASSERT(CheckHotOne_A, $onehot0(gnt_o))
// A grant implies that the sink is ready
`ASSERT(GntImpliesReady_A, |gnt_o |-> ready_i)
// A grant implies that the arbiter asserts valid as well
`ASSERT(GntImpliesValid_A, |gnt_o |-> valid_o)
// A request and a sink that is ready imply a grant
`ASSERT(ReqAndReadyImplyGrant_A, |req_i && ready_i |-> |gnt_o)
// A request and a sink that is ready imply a grant
`ASSERT(ReqImpliesValid_A, |req_i |-> valid_o)
// Both conditions above combined and reversed
`ASSERT(ReadyAndValidImplyGrant_A, ready_i && valid_o |-> |gnt_o)
// Both conditions above combined and reversed
`ASSERT(NoReadyValidNoGrant_A, !(ready_i || valid_o) |-> gnt_o == 0)
// check index / grant correspond
`ASSERT(IndexIsCorrect_A, ready_i && valid_o |-> gnt_o[idx_o] && req_i[idx_o])
if (EnDataPort) begin: gen_data_port_assertion
// data flow
`ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o])
end
endmodule : prim_arbiter_fixed