1 """ Combinatorial Multi-input multiplexer block conforming to Pipeline API
5 from nmigen
import Signal
, Cat
, Const
, Mux
, Module
, Array
6 from nmigen
.cli
import verilog
, rtlil
7 from nmigen
.lib
.coding
import PriorityEncoder
8 from nmigen
.hdl
.rec
import Record
, Layout
10 from collections
.abc
import Sequence
12 from example_buf_pipe
import eq
, NextControl
, PrevControl
, ExampleStage
16 """ Common functions for Pipeline API
18 def __init__(self
, in_multi
=None, p_len
=1):
19 """ Multi-input Control class
22 * add i_data members to PrevControl and
23 * add o_data member to NextControl
26 # set up input and output IO ACK (prev/next ready/valid)
28 for i
in range(p_len
):
29 p
.append(PrevControl(in_multi
))
31 self
.n
= NextControl()
33 def connect_to_next(self
, nxt
, p_idx
=0):
34 """ helper function to connect to the next stage data/valid/ready.
36 return self
.n
.connect_to_next(nxt
.p
[p_idx
])
38 def connect_in(self
, prev
, idx
=0, prev_idx
=None):
39 """ helper function to connect stage to an input source. do not
40 use to connect stage-to-stage!
43 return self
.p
[idx
].connect_in(prev
.p
)
44 return self
.p
[idx
].connect_in(prev
.p
[prev_idx
])
46 def connect_out(self
, nxt
):
47 """ helper function to connect stage to an output source. do not
48 use to connect stage-to-stage!
51 return self
.n
.connect_out(nxt
.n
)
52 return self
.n
.connect_out(nxt
.n
)
54 def set_input(self
, i
, idx
=0):
55 """ helper function to set the input data
57 return eq(self
.p
[idx
].i_data
, i
)
61 for i
in range(len(self
.p
)):
62 res
+= [self
.p
[i
].i_valid
, self
.p
[i
].o_ready
,
63 self
.p
[i
].i_data
]# XXX need flattening!]
64 res
+= [self
.n
.i_ready
, self
.n
.o_valid
,
65 self
.n
.o_data
] # XXX need flattening!]
70 class CombMultiInPipeline(MultiInControl
):
71 """ A multi-input Combinatorial block conforming to the Pipeline API
75 p.i_data : StageInput, shaped according to ispec
77 p.o_data : StageOutput, shaped according to ospec
79 r_data : input_shape according to ispec
80 A temporary (buffered) copy of a prior (valid) input.
81 This is HELD if the output is not ready. It is updated
85 def __init__(self
, stage
, p_len
, p_mux
):
86 MultiInControl
.__init
__(self
, p_len
=p_len
)
90 # set up the input and output data
91 for i
in range(p_len
):
92 self
.p
[i
].i_data
= stage
.ispec() # input type
93 self
.n
.o_data
= stage
.ospec()
95 def elaborate(self
, platform
):
98 m
.submodules
+= self
.p_mux
100 # need an array of buffer registers conforming to *input* spec
106 for i
in range(p_len
):
107 r
= self
.stage
.ispec() # input type
109 data_valid
.append(Signal(name
="data_valid", reset_less
=True))
110 p_i_valid
.append(Signal(name
="p_i_valid", reset_less
=True))
111 n_i_readyn
.append(Signal(name
="n_i_readyn", reset_less
=True))
112 if hasattr(self
.stage
, "setup"):
113 self
.stage
.setup(m
, r
)
115 r_data
= Array(r_data
)
116 p_i_valid
= Array(p_i_valid
)
117 n_i_readyn
= Array(n_i_readyn
)
118 data_valid
= Array(data_valid
)
120 mid
= self
.p_mux
.m_id
121 for i
in range(p_len
):
122 m
.d
.comb
+= data_valid
[i
].eq(0)
123 m
.d
.comb
+= n_i_readyn
[i
].eq(1)
124 m
.d
.comb
+= p_i_valid
[i
].eq(0)
125 m
.d
.comb
+= self
.p
[i
].o_ready
.eq(0)
126 m
.d
.comb
+= p_i_valid
[mid
].eq(self
.p_mux
.active
)
127 m
.d
.comb
+= self
.p
[mid
].o_ready
.eq(~data_valid
[mid
] | self
.n
.i_ready
)
128 m
.d
.comb
+= n_i_readyn
[mid
].eq(~self
.n
.i_ready
& data_valid
[mid
])
129 anyvalid
= Signal(i
, reset_less
=True)
131 for i
in range(p_len
):
132 av
.append(data_valid
[i
])
134 m
.d
.comb
+= self
.n
.o_valid
.eq(anyvalid
.bool())
135 m
.d
.comb
+= data_valid
[mid
].eq(p_i_valid
[mid
] | \
136 (n_i_readyn
[mid
] & data_valid
[mid
]))
138 for i
in range(p_len
):
139 vr
= Signal(reset_less
=True)
140 m
.d
.comb
+= vr
.eq(self
.p
[i
].i_valid
& self
.p
[i
].o_ready
)
142 m
.d
.comb
+= eq(r_data
[i
], self
.p
[i
].i_data
)
144 m
.d
.comb
+= eq(self
.n
.o_data
, self
.stage
.process(r_data
[mid
]))
149 class InputPriorityArbiter
:
150 def __init__(self
, pipe
, num_rows
):
152 self
.num_rows
= num_rows
153 self
.mmax
= int(log(self
.num_rows
) / log(2))
154 self
.m_id
= Signal(self
.mmax
, reset_less
=True) # multiplex id
155 self
.active
= Signal(reset_less
=True)
157 def elaborate(self
, platform
):
160 assert len(self
.pipe
.p
) == self
.num_rows
, \
161 "must declare input to be same size"
162 pe
= PriorityEncoder(self
.num_rows
)
163 m
.submodules
.selector
= pe
165 # connect priority encoder
167 for i
in range(self
.num_rows
):
168 p_i_valid
= Signal(reset_less
=True)
169 m
.d
.comb
+= p_i_valid
.eq(self
.pipe
.p
[i
].i_valid_logic())
170 in_ready
.append(p_i_valid
)
171 m
.d
.comb
+= pe
.i
.eq(Cat(*in_ready
)) # array of input "valids"
172 m
.d
.comb
+= self
.active
.eq(~pe
.n
) # encoder active (one input valid)
173 m
.d
.comb
+= self
.m_id
.eq(pe
.o
) # output one active input
178 return [self
.m_id
, self
.active
]
182 class ExamplePipeline(CombMultiInPipeline
):
183 """ an example of how to use the combinatorial pipeline.
186 def __init__(self
, p_len
=2):
187 p_mux
= InputPriorityArbiter(self
, p_len
)
188 CombMultiInPipeline
.__init
__(self
, ExampleStage
, p_len
, p_mux
)
191 if __name__
== '__main__':
193 dut
= ExamplePipeline()
194 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
195 with
open("test_combpipe.il", "w") as f
: