1 """ Priority Picker: optimised back-to-back PriorityEncoder and Decoder
2 and MultiPriorityPicker: cascading mutually-exclusive pickers
4 PriorityPicker: the input is N bits, the output is N bits wide and
7 MultiPriorityPicker: likewise except that there are M pickers and
8 each output is guaranteed mutually exclusive. Optionally:
9 an "index" (and enable line) is also outputted.
11 MultiPriorityPicker is designed for port-selection, when there are
12 multiple "things" (of width N) contending for access to M "ports".
13 When the M=0 "thing" requests a port, it gets allocated port 0
14 (always). However if the M=0 "thing" does *not* request a port,
15 this gives the M=1 "thing" the opportunity to gain access to port 0.
17 Given that N may potentially be much greater than M (16 bits wide
18 where M may be e.g. only 4) we can't just ok, "ok so M=N therefore
19 M=0 gets access to port 0, M=1 gets access to port 1" etc.
22 from nmigen
import Module
, Signal
, Cat
, Elaboratable
, Array
, Const
, Mux
23 from nmigen
.cli
import verilog
, rtlil
27 class PriorityPicker(Elaboratable
):
28 """ implements a priority-picker. input: N bits, output: N bits
30 * lsb_mode is for a LSB-priority picker
31 * reverse_i=True is for convenient reverseal of the input bits
32 * reverse_o=True is for convenient reversal of the output bits
34 def __init__(self
, wid
, lsb_mode
=False, reverse_i
=False, reverse_o
=False):
37 self
.lsb_mode
= lsb_mode
38 self
.reverse_i
= reverse_i
39 self
.reverse_o
= reverse_o
40 self
.i
= Signal(wid
, reset_less
=True)
41 self
.o
= Signal(wid
, reset_less
=True)
42 self
.en_o
= Signal(reset_less
=True) # true if any output is true
44 def elaborate(self
, platform
):
47 # works by saying, "if all previous bits were zero, we get a chance"
49 ni
= Signal(self
.wid
, reset_less
= True)
53 m
.d
.comb
+= ni
.eq(~
Cat(*i
))
54 prange
= list(range(0, self
.wid
))
58 t
= Signal(name
="t%d" % n
, reset_less
= True)
61 m
.d
.comb
+= t
.eq(i
[n
])
63 m
.d
.comb
+= t
.eq(~
Cat(ni
[n
], *i
[:n
]).bool())
66 # we like Cat(*xxx). turn lists into concatenated bits
67 m
.d
.comb
+= self
.o
.eq(Cat(*res
))
68 # useful "is any output enabled" signal
69 m
.d
.comb
+= self
.en_o
.eq(self
.o
.bool()) # true if 1 input is true
82 class MultiPriorityPicker(Elaboratable
):
83 """ implements a multi-input priority picker
84 Mx inputs of N bits, Mx outputs of N bits, only one is set
86 Each picker masks out the one below it, such that the first
87 gets top priority, the second cannot have the same bit that
88 the first has set, and so on. To do this, a "mask" accumulates
89 the output from the chain, masking the input to the next chain.
91 Also outputted (optional): an index for each picked "thing".
93 def __init__(self
, wid
, levels
, indices
=False, multiin
=False):
96 self
.indices
= indices
97 self
.multiin
= multiin
101 # multiple inputs, multiple outputs.
102 i_l
= [] # array of picker outputs
103 for j
in range(self
.levels
):
104 i
= Signal(self
.wid
, name
="i_%d" % j
, reset_less
=True)
108 # only the one input, but multiple (single) bit outputs
109 self
.i
= Signal(self
.wid
, reset_less
=True)
111 # create array of (single-bit) outputs (unary)
112 o_l
= [] # array of picker outputs
113 for j
in range(self
.levels
):
114 o
= Signal(self
.wid
, name
="o_%d" % j
, reset_less
=True)
118 # add an array of "enables"
119 self
.en_o
= Signal(self
.levels
, name
="en_o", reset_less
=True)
124 # add an array of indices
125 lidx
= math
.ceil(math
.log2(self
.levels
))
126 idx_o
= [] # store the array of indices
127 for j
in range(self
.levels
):
128 i
= Signal(lidx
, name
="idxo_%d" % j
, reset_less
=True)
130 self
.idx_o
= Array(idx_o
)
132 def elaborate(self
, platform
):
136 # create Priority Pickers, accumulate their outputs and prevent
137 # the next one in the chain from selecting that output bit.
138 # the input from the current picker will be "masked" and connected
139 # to the *next* picker on the next loop
143 for j
in range(self
.levels
):
149 pp
= PriorityPicker(self
.wid
)
151 setattr(m
.submodules
, "pp%d" % j
, pp
)
155 p_mask
= Const(0, self
.wid
)
157 mask
= Signal(self
.wid
, name
="m_%d" % j
, reset_less
=True)
158 comb
+= mask
.eq(prev_pp
.o | p_mask
) # accumulate output bits
159 comb
+= pp
.i
.eq(i
& ~mask
) # mask out input
161 i
= pp
.i
# for input to next round
164 # accumulate the enables
166 for j
in range(self
.levels
):
167 en_l
.append(pp_l
[j
].en_o
)
168 # concat accumulated enable bits
169 comb
+= self
.en_o
.eq(Cat(*en_l
))
174 # for each picker enabled, pass that out and set a cascading index
175 lidx
= math
.ceil(math
.log2(self
.levels
))
177 for j
in range(self
.levels
):
179 if prev_count
is None:
180 comb
+= self
.idx_o
[j
].eq(0)
182 count1
= Signal(lidx
, name
="count_%d" % j
, reset_less
=True)
183 comb
+= count1
.eq(prev_count
+ Const(1, lidx
))
184 comb
+= self
.idx_o
[j
].eq(Mux(en_o
, count1
, prev_count
))
185 prev_count
= self
.idx_o
[j
]
198 yield from self
.idx_o
204 if __name__
== '__main__':
205 dut
= PriorityPicker(16)
206 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
207 with
open("test_picker.il", "w") as f
:
209 dut
= MultiPriorityPicker(5, 4, True)
210 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
211 with
open("test_multi_picker.il", "w") as f
:
213 dut
= MultiPriorityPicker(5, 4, False, True)
214 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
215 with
open("test_multi_picker_noidx.il", "w") as f
: