summaryrefslogtreecommitdiff
path: root/src/arb.sv
blob: 15d89b30cdd767e123cae84ad3b3f2ea4750d182 (plain) (blame)
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
module first_set #(
	parameter WIDTH = 32
)(
	input  reg[WIDTH-1 :0] cand,
	output reg[WIDTH-1 :0] selected
);

/* base case */
if (WIDTH == 1) begin :base
	assign selected  = cand;
end

/* recursive case */
else begin :recursive
	localparam SLICE_WIDTH = WIDTH / 2;
	logic[SLICE_WIDTH-1:0] left, right;
	logic pref_left, pref_right;
	logic empty_left, empty_right;

	first_set #(
		.WIDTH(SLICE_WIDTH)
	) first_set_left (
		.cand(cand[SLICE_WIDTH +: SLICE_WIDTH]),
		.selected (left)
	);

	first_set #(
		.WIDTH(SLICE_WIDTH)
	) first_set_right (
		.cand(cand[0 +: SLICE_WIDTH]),
		.selected (right)
	);

	assign selected = right != '0
			? {{SLICE_WIDTH{1'b0}}, right}
			: {left, {SLICE_WIDTH{1'b0}}}
			;
end

always_comb begin
	assert ($onehot0(selected));
end

`ifdef NOSUCHDEF
/* assertions that verilator don't support, apparently */
assert property (WIDTH >= 1)
else $error("input width too narrow");

assert property (1 << $clog2(WIDTH) == WIDTH)
else $error("width not pow2");

`endif

endmodule // arb_recursive

module arb #(
	parameter WIDTH = 32
)(
	input clk,
	input rstx,

	input [WIDTH-1:0] cand,
	output[WIDTH-1:0] selected
);

localparam W2 = 1 << $clog2(WIDTH);
localparam DEPTH = W2 == 1 ? 1 : $clog2(W2);

logic[W2-1:0] w2_cand, w2_selected, mask, next_mask, rot_selected;

/* if there are candidates within the mask of 'above preferred index', use them,
 * otherwise invert the mask and use candidates 'below' preferred index. */
assign w2_cand = ((cand & mask) == '0) ? cand & ~mask : cand & mask;

first_set #(
	.WIDTH(W2)
) first_set_i (
	.cand    (w2_cand),
	.selected(w2_selected)
);

assign selected = w2_selected;

assign rot_selected = {
	w2_selected[0 +: W2-1],
	w2_selected[W2-1]
};

assign next_mask = ~(rot_selected - 1) | rot_selected;

always_ff @(posedge clk or negedge rstx)
if (!rstx) begin
	mask  <= ~0;
end else begin
	if (selected != 0 && mask != next_mask)
		mask <= next_mask;
	else
		mask <= mask;
end

endmodule // arb