blob: e23a76f307bb9145b20b4291fe07a63a9d2e7865 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// The module implements a binary tree to find the maximal entry. the solution
// has O(N) area and O(log(N)) delay complexity, and thus scales well with
// many input sources.
//
// Note that only input values marked as "valid" are respected in the maximum computation.
// If there are multiple valid inputs with the same value, the tree will always select the input
// with the smallest index.
//
// If none of the input values are valid, the output index will be 0 and the output value will
// be equal to the input value at index 0.
`include "prim_assert.sv"
module prim_max_tree #(
parameter int NumSrc = 32,
parameter int Width = 8,
// Derived parameters
localparam int SrcWidth = $clog2(NumSrc)
) (
// The module is combinational - the clock and reset are only used for assertions.
input clk_i,
input rst_ni,
input [NumSrc-1:0][Width-1:0] values_i, // Input values
input [NumSrc-1:0] valid_i, // Input valid bits
output logic [Width-1:0] max_value_o, // Maximum value
output logic [SrcWidth-1:0] max_idx_o, // Index of the maximum value
output logic max_valid_o // Whether any of the inputs is valid
);
///////////////////////
// Binary tree logic //
///////////////////////
// This only works with 2 or more sources.
`ASSERT_INIT(NumSources_A, NumSrc >= 2)
// Align to powers of 2 for simplicity.
// A full binary tree with N levels has 2**N + 2**N-1 nodes.
localparam int NumLevels = $clog2(NumSrc);
logic [2**(NumLevels+1)-2:0] vld_tree;
logic [2**(NumLevels+1)-2:0][SrcWidth-1:0] idx_tree;
logic [2**(NumLevels+1)-2:0][Width-1:0] max_tree;
for (genvar level = 0; level < NumLevels+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 paPa, 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 input values, their corresponding IDs and valid signals to the tree leafs.
if (level == NumLevels) begin : gen_leafs
if (offset < NumSrc) begin : gen_assign
assign vld_tree[Pa] = valid_i[offset];
assign idx_tree[Pa] = offset;
assign max_tree[Pa] = values_i[offset];
end else begin : gen_tie_off
assign vld_tree[Pa] = '0;
assign idx_tree[Pa] = '0;
assign max_tree[Pa] = '0;
end
// This creates the node assignments.
end else begin : gen_nodes
logic sel; // Local helper variable
// In case only one of the parents is valid, forward that one
// In case both parents are valid, forward the one with higher value
assign sel = (~vld_tree[C0] & vld_tree[C1]) |
(vld_tree[C0] & vld_tree[C1] & logic'(max_tree[C1] > max_tree[C0]));
// Forwarding muxes
// Note: these ternaries have triggered a synthesis bug in Vivado versions older
// than 2020.2. If the problem resurfaces again, have a look at issue #1408.
assign vld_tree[Pa] = (sel) ? vld_tree[C1] : vld_tree[C0];
assign idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0];
assign max_tree[Pa] = (sel) ? max_tree[C1] : max_tree[C0];
end
end : gen_level
end : gen_tree
// The results can be found at the tree root
assign max_valid_o = vld_tree[0];
assign max_idx_o = idx_tree[0];
assign max_value_o = max_tree[0];
////////////////
// Assertions //
////////////////
`ifdef INC_ASSERT
//VCS coverage off
// pragma coverage off
// Helper functions for assertions below.
function automatic logic [Width-1:0] max_value (input logic [NumSrc-1:0][Width-1:0] values_i,
input logic [NumSrc-1:0] valid_i);
logic [Width-1:0] value = '0;
for (int k = 0; k < NumSrc; k++) begin
if (valid_i[k] && values_i[k] > value) begin
value = values_i[k];
end
end
return value;
endfunction : max_value
function automatic logic [SrcWidth-1:0] max_idx (input logic [NumSrc-1:0][Width-1:0] values_i,
input logic [NumSrc-1:0] valid_i);
logic [Width-1:0] value = '0;
logic [SrcWidth-1:0] idx = '0;
for (int k = NumSrc-1; k >= 0; k--) begin
if (valid_i[k] && values_i[k] >= value) begin
value = values_i[k];
idx = k;
end
end
return idx;
endfunction : max_idx
logic [Width-1:0] max_value_exp;
logic [SrcWidth-1:0] max_idx_exp;
assign max_value_exp = max_value(values_i, valid_i);
assign max_idx_exp = max_idx(values_i, valid_i);
//VCS coverage on
// pragma coverage on
// TODO(10588): Below syntax is not supported in xcelium, track xcelium cases #46591452.
// `ASSERT(ValidInImpliesValidOut_A, |valid_i <-> max_valid_o)
`ASSERT(ValidInImpliesValidOut_A, |valid_i === max_valid_o)
`ASSERT(MaxComputation_A, max_valid_o |-> max_value_o == max_value_exp)
`ASSERT(MaxComputationInvalid_A, !max_valid_o |-> max_value_o == values_i[0])
`ASSERT(MaxIndexComputation_A, max_valid_o |-> max_idx_o == max_idx_exp)
`ASSERT(MaxIndexComputationInvalid_A, !max_valid_o |-> max_idx_o == '0)
`endif
endmodule : prim_max_tree