1 """ key strategic example showing how to do multi-input fan-in into a
2 multi-stage pipeline, then multi-output fanout, with an unary muxid
5 the multiplex ID from the fan-in is passed in to the pipeline, preserved,
6 and used as a routing ID on the fanout.
9 from random
import randint
11 from nmigen
import Module
, Signal
, Cat
, Value
, Elaboratable
12 from nmigen
.compat
.sim
import run_simulation
13 from nmigen
.cli
import verilog
, rtlil
15 from nmutil
.multipipe
import CombMultiOutPipeline
, CombMuxOutPipe
16 from nmutil
.multipipe
import PriorityCombMuxInPipe
17 from nmutil
.singlepipe
import MaskCancellable
, RecordObject
, Object
20 class PassData2(RecordObject
):
22 RecordObject
.__init
__(self
)
23 self
.muxid
= Signal(2, reset_less
=True)
24 self
.idx
= Signal(8, reset_less
=True)
25 self
.data
= Signal(16, reset_less
=True)
28 class PassData(Object
):
31 self
.muxid
= Signal(2, reset_less
=True)
32 self
.idx
= Signal(8, reset_less
=True)
33 self
.data
= Signal(16, reset_less
=True)
37 class PassThroughStage
:
41 return self
.ispec() # same as ospec
44 return i
# pass-through
48 class PassThroughPipe(MaskCancellable
):
49 def __init__(self
, maskwid
):
50 MaskCancellable
.__init
__(self
, PassThroughStage(), maskwid
)
54 def __init__(self
, dut
, tlen
):
60 for muxid
in range(dut
.num_rows
):
64 for i
in range(self
.tlen
):
65 self
.di
[muxid
][i
] = randint(0, 255) + (muxid
<<8)
66 self
.do
[muxid
][i
] = self
.di
[muxid
][i
]
68 def send(self
, muxid
):
69 for i
in range(self
.tlen
):
70 op2
= self
.di
[muxid
][i
]
71 rs
= self
.dut
.p
[muxid
]
72 yield rs
.valid_i
.eq(1)
73 yield rs
.data_i
.data
.eq(op2
)
74 yield rs
.data_i
.idx
.eq(i
)
75 yield rs
.data_i
.muxid
.eq(muxid
)
78 o_p_ready
= yield rs
.ready_o
81 o_p_ready
= yield rs
.ready_o
83 print ("send", muxid
, i
, hex(op2
), op2
)
84 self
.sent
[muxid
].append(i
)
86 yield rs
.valid_i
.eq(0)
88 # wait until it's received
89 while i
in self
.do
[muxid
]:
92 # wait random period of time before queueing another value
93 for i
in range(randint(0, 3)):
96 yield rs
.valid_i
.eq(0)
99 print ("send ended", muxid
)
101 ## wait random period of time before queueing another value
102 #for i in range(randint(0, 3)):
105 #send_range = randint(0, 3)
109 # send = randint(0, send_range) != 0
111 def rcv(self
, muxid
):
112 rs
= self
.dut
.p
[muxid
]
116 if self
.sent
[muxid
] and randint(0, 2) == 0:
117 todel
= self
.sent
[muxid
].pop()
118 print ("to delete", muxid
, self
.sent
[muxid
], todel
)
119 if todel
in self
.do
[muxid
]:
120 del self
.do
[muxid
][todel
]
121 yield rs
.stop_i
.eq(1)
122 print ("left", muxid
, self
.do
[muxid
])
123 if len(self
.do
[muxid
]) == 0:
126 stall_range
= randint(0, 3)
127 for j
in range(randint(1,10)):
128 stall
= randint(0, stall_range
) != 0
129 yield self
.dut
.n
[0].ready_i
.eq(stall
)
132 n
= self
.dut
.n
[muxid
]
133 yield n
.ready_i
.eq(1)
135 yield rs
.stop_i
.eq(0) # resets cancel mask
136 o_n_valid
= yield n
.valid_o
137 i_n_ready
= yield n
.ready_i
138 if not o_n_valid
or not i_n_ready
:
141 out_muxid
= yield n
.data_o
.muxid
142 out_i
= yield n
.data_o
.idx
143 out_v
= yield n
.data_o
.data
145 print ("recv", out_muxid
, out_i
, hex(out_v
), out_v
)
147 # see if this output has occurred already, delete it if it has
148 assert muxid
== out_muxid
, \
149 "out_muxid %d not correct %d" % (out_muxid
, muxid
)
150 if out_i
not in self
.sent
[muxid
]:
151 print ("cancelled/recv", muxid
, out_i
)
153 assert out_i
in self
.do
[muxid
], "out_i %d not in array %s" % \
154 (out_i
, repr(self
.do
[muxid
]))
155 assert self
.do
[muxid
][out_i
] == out_v
# pass-through data
156 del self
.do
[muxid
][out_i
]
157 todel
= self
.sent
[muxid
].index(out_i
)
158 del self
.sent
[muxid
][todel
]
160 # check if there's any more outputs
161 if len(self
.do
[muxid
]) == 0:
164 print ("recv ended", muxid
)
167 class TestPriorityMuxPipe(PriorityCombMuxInPipe
):
168 def __init__(self
, num_rows
):
169 self
.num_rows
= num_rows
170 stage
= PassThroughStage()
171 PriorityCombMuxInPipe
.__init
__(self
, stage
,
172 p_len
=self
.num_rows
, maskwid
=1)
175 class TestMuxOutPipe(CombMuxOutPipe
):
176 def __init__(self
, num_rows
):
177 self
.num_rows
= num_rows
178 stage
= PassThroughStage()
179 CombMuxOutPipe
.__init
__(self
, stage
, n_len
=self
.num_rows
,
183 class TestInOutPipe(Elaboratable
):
184 def __init__(self
, num_rows
=4):
185 self
.num_rows
= nr
= num_rows
186 self
.inpipe
= TestPriorityMuxPipe(nr
) # fan-in (combinatorial)
187 self
.pipe1
= PassThroughPipe(nr
) # stage 1 (clock-sync)
188 self
.pipe2
= PassThroughPipe(nr
) # stage 2 (clock-sync)
189 self
.pipe3
= PassThroughPipe(nr
) # stage 3 (clock-sync)
190 self
.pipe4
= PassThroughPipe(nr
) # stage 4 (clock-sync)
191 self
.outpipe
= TestMuxOutPipe(nr
) # fan-out (combinatorial)
193 self
.p
= self
.inpipe
.p
# kinda annoying,
194 self
.n
= self
.outpipe
.n
# use pipe in/out as this class in/out
195 self
._ports
= self
.inpipe
.ports() + self
.outpipe
.ports()
197 def elaborate(self
, platform
):
199 m
.submodules
.inpipe
= self
.inpipe
200 m
.submodules
.pipe1
= self
.pipe1
201 m
.submodules
.pipe2
= self
.pipe2
202 m
.submodules
.pipe3
= self
.pipe3
203 m
.submodules
.pipe4
= self
.pipe4
204 m
.submodules
.outpipe
= self
.outpipe
206 m
.d
.comb
+= self
.inpipe
.n
.connect_to_next(self
.pipe1
.p
)
207 m
.d
.comb
+= self
.pipe1
.connect_to_next(self
.pipe2
)
208 m
.d
.comb
+= self
.pipe2
.connect_to_next(self
.pipe3
)
209 m
.d
.comb
+= self
.pipe3
.connect_to_next(self
.pipe4
)
210 m
.d
.comb
+= self
.pipe4
.connect_to_next(self
.outpipe
)
219 dut
= TestInOutPipe()
220 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
221 with
open("test_inoutmux_unarycancel_pipe.il", "w") as f
:
226 test
= InputTest(dut
, tlen
)
227 run_simulation(dut
, [test
.rcv(1), test
.rcv(0),
228 test
.rcv(3), test
.rcv(2),
229 test
.send(0), test
.send(1),
230 test
.send(3), test
.send(2),
232 vcd_name
="test_inoutmux_unarycancel_pipe.vcd")
234 if __name__
== '__main__':