1 """ key strategic example showing how to do multi-input fan-in into a
2 multi-stage pipeline, then multi-output fanout.
4 the multiplex ID from the fan-in is passed in to the pipeline, preserved,
5 and used as a routing ID on the fanout.
8 from random
import randint
10 from nmigen
import Module
, Signal
, Cat
, Value
11 from nmigen
.compat
.sim
import run_simulation
12 from nmigen
.cli
import verilog
, rtlil
14 from multipipe
import CombMultiOutPipeline
, CombMuxOutPipe
15 from multipipe
import PriorityCombMuxInPipe
16 from singlepipe
import SimpleHandshake
, RecordObject
19 class PassData2(RecordObject
):
21 RecordObject
.__init
__(self
)
22 self
.mid
= Signal(2, reset_less
=True)
23 self
.idx
= Signal(8, reset_less
=True)
24 self
.data
= Signal(16, reset_less
=True)
29 self
.mid
= Signal(2, reset_less
=True)
30 self
.idx
= Signal(8, reset_less
=True)
31 self
.data
= Signal(16, reset_less
=True)
40 for elem_bits
, elem_sign
in (elem
.shape() for elem
in self
.ports()):
41 bits
= max(bits
, elem_bits
+ elem_sign
)
42 sign
= max(sign
, elem_sign
)
46 return [self
.mid
.eq(i
.mid
), self
.idx
.eq(i
.idx
), self
.data
.eq(i
.data
)]
52 class PassThroughStage
:
56 return self
.ispec() # same as ospec
59 return i
# pass-through
63 class PassThroughPipe(SimpleHandshake
):
65 SimpleHandshake
.__init
__(self
, PassThroughStage())
69 def __init__(self
, dut
):
74 for mid
in range(dut
.num_rows
):
77 for i
in range(self
.tlen
):
78 self
.di
[mid
][i
] = randint(0, 255) + (mid
<<8)
79 self
.do
[mid
][i
] = self
.di
[mid
][i
]
82 for i
in range(self
.tlen
):
85 yield rs
.i_valid
.eq(1)
86 yield rs
.i_data
.data
.eq(op2
)
87 yield rs
.i_data
.idx
.eq(i
)
88 yield rs
.i_data
.mid
.eq(mid
)
90 o_p_ready
= yield rs
.o_ready
93 o_p_ready
= yield rs
.o_ready
95 print ("send", mid
, i
, hex(op2
))
97 yield rs
.i_valid
.eq(0)
98 # wait random period of time before queueing another value
99 for i
in range(randint(0, 3)):
102 yield rs
.i_valid
.eq(0)
105 print ("send ended", mid
)
107 ## wait random period of time before queueing another value
108 #for i in range(randint(0, 3)):
111 #send_range = randint(0, 3)
115 # send = randint(0, send_range) != 0
119 #stall_range = randint(0, 3)
120 #for j in range(randint(1,10)):
121 # stall = randint(0, stall_range) != 0
122 # yield self.dut.n[0].i_ready.eq(stall)
125 yield n
.i_ready
.eq(1)
127 o_n_valid
= yield n
.o_valid
128 i_n_ready
= yield n
.i_ready
129 if not o_n_valid
or not i_n_ready
:
132 out_mid
= yield n
.o_data
.mid
133 out_i
= yield n
.o_data
.idx
134 out_v
= yield n
.o_data
.data
136 print ("recv", out_mid
, out_i
, hex(out_v
))
138 # see if this output has occurred already, delete it if it has
139 assert mid
== out_mid
, "out_mid %d not correct %d" % (out_mid
, mid
)
140 assert out_i
in self
.do
[mid
], "out_i %d not in array %s" % \
141 (out_i
, repr(self
.do
[mid
]))
142 assert self
.do
[mid
][out_i
] == out_v
# pass-through data
143 del self
.do
[mid
][out_i
]
145 # check if there's any more outputs
146 if len(self
.do
[mid
]) == 0:
148 print ("recv ended", mid
)
151 class TestPriorityMuxPipe(PriorityCombMuxInPipe
):
152 def __init__(self
, num_rows
):
153 self
.num_rows
= num_rows
154 stage
= PassThroughStage()
155 PriorityCombMuxInPipe
.__init
__(self
, stage
, p_len
=self
.num_rows
)
159 def __init__(self
, dut
):
164 for i
in range(self
.tlen
* dut
.num_rows
):
168 mid
= randint(0, dut
.num_rows
-1)
169 data
= randint(0, 255) + (mid
<<8)
172 for i
in range(self
.tlen
* dut
.num_rows
):
176 yield rs
.i_valid
.eq(1)
177 yield rs
.i_data
.data
.eq(op2
)
178 yield rs
.i_data
.mid
.eq(mid
)
180 o_p_ready
= yield rs
.o_ready
183 o_p_ready
= yield rs
.o_ready
185 print ("send", mid
, i
, hex(op2
))
187 yield rs
.i_valid
.eq(0)
188 # wait random period of time before queueing another value
189 for i
in range(randint(0, 3)):
192 yield rs
.i_valid
.eq(0)
195 class TestMuxOutPipe(CombMuxOutPipe
):
196 def __init__(self
, num_rows
):
197 self
.num_rows
= num_rows
198 stage
= PassThroughStage()
199 CombMuxOutPipe
.__init
__(self
, stage
, n_len
=self
.num_rows
)
203 def __init__(self
, num_rows
=4):
204 self
.num_rows
= num_rows
205 self
.inpipe
= TestPriorityMuxPipe(num_rows
) # fan-in (combinatorial)
206 self
.pipe1
= PassThroughPipe() # stage 1 (clock-sync)
207 self
.pipe2
= PassThroughPipe() # stage 2 (clock-sync)
208 self
.outpipe
= TestMuxOutPipe(num_rows
) # fan-out (combinatorial)
210 self
.p
= self
.inpipe
.p
# kinda annoying,
211 self
.n
= self
.outpipe
.n
# use pipe in/out as this class in/out
212 self
._ports
= self
.inpipe
.ports() + self
.outpipe
.ports()
214 def elaborate(self
, platform
):
216 m
.submodules
.inpipe
= self
.inpipe
217 m
.submodules
.pipe1
= self
.pipe1
218 m
.submodules
.pipe2
= self
.pipe2
219 m
.submodules
.outpipe
= self
.outpipe
221 m
.d
.comb
+= self
.inpipe
.n
.connect_to_next(self
.pipe1
.p
)
222 m
.d
.comb
+= self
.pipe1
.connect_to_next(self
.pipe2
)
223 m
.d
.comb
+= self
.pipe2
.connect_to_next(self
.outpipe
)
231 if __name__
== '__main__':
232 dut
= TestInOutPipe()
233 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
234 with
open("test_inoutmux_pipe.il", "w") as f
:
236 #run_simulation(dut, testbench(dut), vcd_name="test_inputgroup.vcd")
238 test
= InputTest(dut
)
239 run_simulation(dut
, [test
.rcv(1), test
.rcv(0),
240 test
.rcv(3), test
.rcv(2),
241 test
.send(0), test
.send(1),
242 test
.send(3), test
.send(2),
244 vcd_name
="test_inoutmux_pipe.vcd")