-
Notifications
You must be signed in to change notification settings - Fork 0
/
lzc.sv
113 lines (98 loc) · 4.3 KB
/
lzc.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright 2018 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
/// A trailing zero counter / leading zero counter.
/// Set MODE to 0 for trailing zero counter => cnt_o is the number of trailing zeros (from the LSB)
/// Set MODE to 1 for leading zero counter => cnt_o is the number of leading zeros (from the MSB)
/// If the input does not contain a zero, `empty_o` is asserted. Additionally `cnt_o` contains
/// the maximum number of zeros - 1. For example:
/// in_i = 000_0000, empty_o = 1, cnt_o = 6 (mode = 0)
/// in_i = 000_0001, empty_o = 0, cnt_o = 0 (mode = 0)
/// in_i = 000_1000, empty_o = 0, cnt_o = 3 (mode = 0)
/// Furthermore, this unit contains a more efficient implementation for Verilator (simulation only).
/// This speeds up simulation significantly.
module lzc #(
/// The width of the input vector.
parameter int unsigned WIDTH = 2,
/// Mode selection: 0 -> trailing zero, 1 -> leading zero
parameter bit MODE = 1'b0,
/// Dependent parameter. Do **not** change!
///
/// Width of the output signal with the zero count.
parameter int unsigned CNT_WIDTH = cf_math_pkg::idx_width(WIDTH)
) (
/// Input vector to be counted.
input logic [WIDTH-1:0] in_i,
/// Count of the leading / trailing zeros.
output logic [CNT_WIDTH-1:0] cnt_o,
/// Counter is empty: Asserted if all bits in in_i are zero.
output logic empty_o
);
if (WIDTH == 1) begin : gen_degenerate_lzc
assign cnt_o[0] = !in_i[0];
assign empty_o = !in_i[0];
end else begin : gen_lzc
localparam int unsigned NumLevels = $clog2(WIDTH);
`ifndef VERILATOR
// pragma translate_off
initial begin
assert(WIDTH > 0) else $fatal(1, "input must be at least one bit wide");
end
// pragma translate_on
`endif
logic [WIDTH-1:0][NumLevels-1:0] index_lut;
logic [2**NumLevels-1:0] sel_nodes;
logic [2**NumLevels-1:0][NumLevels-1:0] index_nodes;
logic [WIDTH-1:0] in_tmp;
// reverse vector if required
always_comb begin : flip_vector
for (int unsigned i = 0; i < WIDTH; i++) begin
in_tmp[i] = (MODE) ? in_i[WIDTH-1-i] : in_i[i];
end
end
for (genvar j = 0; unsigned'(j) < WIDTH; j++) begin : g_index_lut
assign index_lut[j] = (NumLevels)'(unsigned'(j));
end
for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : g_levels
if (unsigned'(level) == NumLevels - 1) begin : g_last_level
for (genvar k = 0; k < 2 ** level; k++) begin : g_level
// if two successive indices are still in the vector...
if (unsigned'(k) * 2 < WIDTH - 1) begin : g_reduce
assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2] | in_tmp[k * 2 + 1];
assign index_nodes[2 ** level - 1 + k] = (in_tmp[k * 2] == 1'b1)
? index_lut[k * 2] :
index_lut[k * 2 + 1];
end
// if only the first index is still in the vector...
if (unsigned'(k) * 2 == WIDTH - 1) begin : g_base
assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2];
assign index_nodes[2 ** level - 1 + k] = index_lut[k * 2];
end
// if index is out of range
if (unsigned'(k) * 2 > WIDTH - 1) begin : g_out_of_range
assign sel_nodes[2 ** level - 1 + k] = 1'b0;
assign index_nodes[2 ** level - 1 + k] = '0;
end
end
end else begin : g_not_last_level
for (genvar l = 0; l < 2 ** level; l++) begin : g_level
assign sel_nodes[2 ** level - 1 + l] =
sel_nodes[2 ** (level + 1) - 1 + l * 2] | sel_nodes[2 ** (level + 1) - 1 + l * 2 + 1];
assign index_nodes[2 ** level - 1 + l] = (sel_nodes[2 ** (level + 1) - 1 + l * 2] == 1'b1)
? index_nodes[2 ** (level + 1) - 1 + l * 2] :
index_nodes[2 ** (level + 1) - 1 + l * 2 + 1];
end
end
end
assign cnt_o = NumLevels > unsigned'(0) ? index_nodes[0] : {($clog2(WIDTH)) {1'b0}};
assign empty_o = NumLevels > unsigned'(0) ? ~sel_nodes[0] : ~(|in_i);
end : gen_lzc
// pragma translate_off
`ifndef VERILATOR
initial begin: validate_params
assert (WIDTH >= 1)
else $fatal(1, "The WIDTH must at least be one bit wide!");
end
`endif
// pragma translate_on
endmodule : lzc