1 """ Unit tests for Buffered and Unbuffered pipelines
3 contains useful worked examples of how to use the Pipeline API,
6 * Combinatorial Stage "Chaining"
7 * class-based data stages
8 * nmigen module-based data stages
9 * special nmigen module-based data stage, where the stage *is* the module
10 * Record-based data stages
11 * static-class data stages
12 * multi-stage pipelines (and how to connect them)
13 * how to *use* the pipelines (see Test5) - how to get data in and out
17 from nmigen
import Module
, Signal
, Mux
, Const
, Elaboratable
18 from nmigen
.hdl
.rec
import Record
19 from nmigen
.compat
.sim
import run_simulation
20 from nmigen
.cli
import verilog
, rtlil
22 from nmutil
.test
.example_buf_pipe
import ExampleBufPipe
, ExampleBufPipeAdd
23 from nmutil
.test
.example_buf_pipe
import ExamplePipeline
, UnbufferedPipeline
24 from nmutil
.test
.example_buf_pipe
import ExampleStageCls
25 from nmutil
.iocontrol
import PrevControl
, NextControl
26 from nmutil
.stageapi
import StageChain
, StageCls
27 from nmutil
.singlepipe
import ControlBase
28 from nmutil
.singlepipe
import UnbufferedPipeline2
29 from nmutil
.singlepipe
import SimpleHandshake
30 from nmutil
.singlepipe
import BufferedHandshake
31 from nmutil
.singlepipe
import PassThroughHandshake
32 from nmutil
.singlepipe
import PassThroughStage
33 from nmutil
.singlepipe
import FIFOControl
34 from nmutil
.singlepipe
import RecordObject
35 from nmutil
.singlepipe
import MaskCancellable
37 from random
import randint
, seed
44 def check_o_n_valid(dut
, val
):
45 o_n_valid
= yield dut
.n
.o_valid
46 assert o_n_valid
== val
49 def check_o_n_valid2(dut
, val
):
50 o_n_valid
= yield dut
.n
.o_valid
51 assert o_n_valid
== val
55 # yield dut.i_p_rst.eq(1)
56 yield dut
.n
.i_ready
.eq(0)
57 # yield dut.p.o_ready.eq(0)
60 # yield dut.i_p_rst.eq(0)
61 yield dut
.n
.i_ready
.eq(1)
62 yield dut
.p
.i_data
.eq(5)
63 yield dut
.p
.i_valid
.eq(1)
66 yield dut
.p
.i_data
.eq(7)
67 yield from check_o_n_valid(dut
, 0) # effects of i_p_valid delayed
69 yield from check_o_n_valid(dut
, 1) # ok *now* i_p_valid effect is felt
71 yield dut
.p
.i_data
.eq(2)
73 # begin going into "stall" (next stage says ready)
74 yield dut
.n
.i_ready
.eq(0)
75 yield dut
.p
.i_data
.eq(9)
77 yield dut
.p
.i_valid
.eq(0)
78 yield dut
.p
.i_data
.eq(12)
80 yield dut
.p
.i_data
.eq(32)
81 yield dut
.n
.i_ready
.eq(1)
83 yield from check_o_n_valid(dut
, 1) # buffer still needs to output
85 yield from check_o_n_valid(dut
, 1) # buffer still needs to output
87 yield from check_o_n_valid(dut
, 0) # buffer outputted, *now* we're done.
92 # yield dut.p.i_rst.eq(1)
93 yield dut
.n
.i_ready
.eq(0)
94 # yield dut.p.o_ready.eq(0)
97 # yield dut.p.i_rst.eq(0)
98 yield dut
.n
.i_ready
.eq(1)
99 yield dut
.p
.i_data
.eq(5)
100 yield dut
.p
.i_valid
.eq(1)
103 yield dut
.p
.i_data
.eq(7)
104 # effects of i_p_valid delayed 2 clocks
105 yield from check_o_n_valid2(dut
, 0)
107 # effects of i_p_valid delayed 2 clocks
108 yield from check_o_n_valid2(dut
, 0)
110 yield dut
.p
.i_data
.eq(2)
112 yield from check_o_n_valid2(dut
, 1) # ok *now* i_p_valid effect is felt
113 # begin going into "stall" (next stage says ready)
114 yield dut
.n
.i_ready
.eq(0)
115 yield dut
.p
.i_data
.eq(9)
117 yield dut
.p
.i_valid
.eq(0)
118 yield dut
.p
.i_data
.eq(12)
120 yield dut
.p
.i_data
.eq(32)
121 yield dut
.n
.i_ready
.eq(1)
123 yield from check_o_n_valid2(dut
, 1) # buffer still needs to output
125 yield from check_o_n_valid2(dut
, 1) # buffer still needs to output
127 yield from check_o_n_valid2(dut
, 1) # buffer still needs to output
129 yield from check_o_n_valid2(dut
, 0) # buffer outputted, *now* we're done.
136 def __init__(self
, dut
, resultfn
):
138 self
.resultfn
= resultfn
140 for i
in range(num_tests
):
141 #data.append(randint(0, 1<<16-1))
142 self
.data
.append(i
+1)
147 while self
.o
!= len(self
.data
):
148 send_range
= randint(0, 3)
149 for j
in range(randint(1, 10)):
153 send
= randint(0, send_range
) != 0
154 o_p_ready
= yield self
.dut
.p
.o_ready
158 if send
and self
.i
!= len(self
.data
):
159 yield self
.dut
.p
.i_valid
.eq(1)
160 yield self
.dut
.p
.i_data
.eq(self
.data
[self
.i
])
163 yield self
.dut
.p
.i_valid
.eq(0)
167 while self
.o
!= len(self
.data
):
168 stall_range
= randint(0, 3)
169 for j
in range(randint(1, 10)):
170 stall
= randint(0, stall_range
) != 0
171 yield self
.dut
.n
.i_ready
.eq(stall
)
173 o_n_valid
= yield self
.dut
.n
.o_valid
174 i_n_ready
= yield self
.dut
.n
.i_ready_test
175 if not o_n_valid
or not i_n_ready
:
177 o_data
= yield self
.dut
.n
.o_data
178 self
.resultfn(o_data
, self
.data
[self
.o
], self
.i
, self
.o
)
180 if self
.o
== len(self
.data
):
184 def resultfn_3(o_data
, expected
, i
, o
):
185 assert o_data
== expected
+ 1, \
186 "%d-%d data %x not match %x\n" \
187 % (i
, o
, o_data
, expected
)
190 def data_placeholder():
192 for i
in range(num_tests
):
194 d
.src1
= randint(0, 1 << 16-1)
195 d
.src2
= randint(0, 1 << 16-1)
202 for i
in range(num_tests
):
203 data
.append({'src1': randint(0, 1 << 16-1),
204 'src2': randint(0, 1 << 16-1)})
209 def __init__(self
, dut
, resultfn
, data
=None, stage_ctl
=False):
211 self
.resultfn
= resultfn
212 self
.stage_ctl
= stage_ctl
217 for i
in range(num_tests
):
219 (randint(0, 1 << 16-1), randint(0, 1 << 16-1)))
224 while self
.o
!= len(self
.data
):
225 send_range
= randint(0, 3)
226 for j
in range(randint(1, 10)):
230 send
= randint(0, send_range
) != 0
232 o_p_ready
= yield self
.dut
.p
.o_ready
236 if send
and self
.i
!= len(self
.data
):
237 yield self
.dut
.p
.i_valid
.eq(1)
238 for v
in self
.dut
.set_input(self
.data
[self
.i
]):
242 yield self
.dut
.p
.i_valid
.eq(0)
246 while self
.o
!= len(self
.data
):
247 stall_range
= randint(0, 3)
248 for j
in range(randint(1, 10)):
249 ready
= randint(0, stall_range
) != 0
251 yield self
.dut
.n
.i_ready
.eq(ready
)
253 o_n_valid
= yield self
.dut
.n
.o_valid
254 i_n_ready
= yield self
.dut
.n
.i_ready_test
255 if not o_n_valid
or not i_n_ready
:
257 if isinstance(self
.dut
.n
.o_data
, Record
):
259 dod
= self
.dut
.n
.o_data
260 for k
, v
in dod
.fields
.items():
263 o_data
= yield self
.dut
.n
.o_data
264 self
.resultfn(o_data
, self
.data
[self
.o
], self
.i
, self
.o
)
266 if self
.o
== len(self
.data
):
271 def __init__(self
, dut
, resultfn
, maskwid
, data
=None, stage_ctl
=False,
274 self
.resultfn
= resultfn
275 self
.stage_ctl
= stage_ctl
276 self
.maskwid
= maskwid
277 self
.latching
= latching
283 for i
in range(num_tests
):
285 (randint(0, 1 << 16-1), randint(0, 1 << 16-1)))
290 while self
.o
!= len(self
.data
):
291 send_range
= randint(0, 3)
292 for j
in range(randint(1, 10)):
296 send
= randint(0, send_range
) != 0
298 o_p_ready
= yield self
.dut
.p
.o_ready
304 latchtest
= randint(0, 3) == 0
306 yield self
.dut
.p
.i_valid
.eq(0)
307 yield self
.dut
.p
.mask_i
.eq(0)
308 # wait for pipeline to flush, then invert state
311 self
.latchmode
= 1 - self
.latchmode
312 yield self
.dut
.latchmode
.eq(self
.latchmode
)
313 mode
= yield self
.dut
.latchmode
314 print("latching", mode
)
316 if send
and self
.i
!= len(self
.data
):
317 print("send", self
.i
, self
.data
[self
.i
])
318 yield self
.dut
.p
.i_valid
.eq(1)
319 yield self
.dut
.p
.mask_i
.eq(1 << self
.i
) # XXX TODO
320 for v
in self
.dut
.set_input(self
.data
[self
.i
]):
324 yield self
.dut
.p
.i_valid
.eq(0)
325 yield self
.dut
.p
.mask_i
.eq(0) # XXX TODO
329 while self
.o
!= len(self
.data
):
330 stall_range
= randint(0, 3)
331 for j
in range(randint(1, 10)):
332 ready
= randint(0, stall_range
) != 0
334 yield self
.dut
.n
.i_ready
.eq(ready
)
336 o_n_valid
= yield self
.dut
.n
.o_valid
337 i_n_ready
= yield self
.dut
.n
.i_ready_test
338 if not o_n_valid
or not i_n_ready
:
340 if isinstance(self
.dut
.n
.o_data
, Record
):
342 dod
= self
.dut
.n
.o_data
343 for k
, v
in dod
.fields
.items():
346 o_data
= yield self
.dut
.n
.o_data
347 print("recv", self
.o
, o_data
)
348 self
.resultfn(o_data
, self
.data
[self
.o
], self
.i
, self
.o
)
350 if self
.o
== len(self
.data
):
354 def resultfn_5(o_data
, expected
, i
, o
):
355 res
= expected
[0] + expected
[1]
356 assert o_data
== res
, \
357 "%d-%d data %x not match %s\n" \
358 % (i
, o
, o_data
, repr(expected
))
363 for i
in range(num_tests
):
364 #data.append(randint(0, 1<<16-1))
369 stall
= randint(0, 3) != 0
370 send
= randint(0, 5) != 0
371 yield dut
.n
.i_ready
.eq(stall
)
372 o_p_ready
= yield dut
.p
.o_ready
374 if send
and i
!= len(data
):
375 yield dut
.p
.i_valid
.eq(1)
376 yield dut
.p
.i_data
.eq(data
[i
])
379 yield dut
.p
.i_valid
.eq(0)
381 o_n_valid
= yield dut
.n
.o_valid
382 i_n_ready
= yield dut
.n
.i_ready_test
383 if o_n_valid
and i_n_ready
:
384 o_data
= yield dut
.n
.o_data
385 assert o_data
== data
[o
] + 2, "%d-%d data %x not match %x\n" \
386 % (i
, o
, o_data
, data
[o
])
391 ######################################################################
393 ######################################################################
396 class ExampleBufPipe2(ControlBase
):
397 """ Example of how to do chained pipeline stages.
400 def elaborate(self
, platform
):
401 m
= ControlBase
.elaborate(self
, platform
)
403 pipe1
= ExampleBufPipe()
404 pipe2
= ExampleBufPipe()
406 m
.submodules
.pipe1
= pipe1
407 m
.submodules
.pipe2
= pipe2
409 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
414 ######################################################################
416 ######################################################################
418 class ExampleBufPipeChain2(BufferedHandshake
):
419 """ connects two stages together as a *single* combinatorial stage.
423 stage1
= ExampleStageCls()
424 stage2
= ExampleStageCls()
425 combined
= StageChain([stage1
, stage2
])
426 BufferedHandshake
.__init
__(self
, combined
)
431 for i
in range(num_tests
):
432 data
.append(randint(0, 1 << 16-2))
436 def resultfn_9(o_data
, expected
, i
, o
):
438 assert o_data
== res
, \
439 "%d-%d received data %x not match expected %x\n" \
440 % (i
, o
, o_data
, res
)
443 ######################################################################
445 ######################################################################
447 class SetLessThan(Elaboratable
):
448 def __init__(self
, width
, signed
):
450 self
.src1
= Signal((width
, signed
), name
="src1")
451 self
.src2
= Signal((width
, signed
), name
="src2")
452 self
.output
= Signal(width
, name
="out")
454 def elaborate(self
, platform
):
455 self
.m
.d
.comb
+= self
.output
.eq(Mux(self
.src1
< self
.src2
, 1, 0))
459 class LTStage(StageCls
):
460 """ module-based stage example
464 self
.slt
= SetLessThan(16, True)
466 def ispec(self
, name
):
467 return (Signal(16, name
="%s_sig1" % name
),
468 Signal(16, name
="%s_sig2" % name
))
470 def ospec(self
, name
):
471 return Signal(16, name
="%s_out" % name
)
473 def setup(self
, m
, i
):
475 m
.submodules
.slt
= self
.slt
476 m
.d
.comb
+= self
.slt
.src1
.eq(i
[0])
477 m
.d
.comb
+= self
.slt
.src2
.eq(i
[1])
478 m
.d
.comb
+= self
.o
.eq(self
.slt
.output
)
480 def process(self
, i
):
484 class LTStageDerived(SetLessThan
, StageCls
):
485 """ special version of a nmigen module where the module is also a stage
487 shows that you don't actually need to combinatorially connect
488 to the outputs, or add the module as a submodule: just return
489 the module output parameter(s) from the Stage.process() function
493 SetLessThan
.__init
__(self
, 16, True)
496 return (Signal(16), Signal(16))
501 def setup(self
, m
, i
):
502 m
.submodules
.slt
= self
503 m
.d
.comb
+= self
.src1
.eq(i
[0])
504 m
.d
.comb
+= self
.src2
.eq(i
[1])
506 def process(self
, i
):
510 class ExampleLTPipeline(UnbufferedPipeline
):
511 """ an example of how to use the unbuffered pipeline.
516 UnbufferedPipeline
.__init
__(self
, stage
)
519 class ExampleLTBufferedPipeDerived(BufferedHandshake
):
520 """ an example of how to use the buffered pipeline.
524 stage
= LTStageDerived()
525 BufferedHandshake
.__init
__(self
, stage
)
528 def resultfn_6(o_data
, expected
, i
, o
):
529 res
= 1 if expected
[0] < expected
[1] else 0
530 assert o_data
== res
, \
531 "%d-%d data %x not match %s\n" \
532 % (i
, o
, o_data
, repr(expected
))
535 ######################################################################
537 ######################################################################
539 class ExampleAddRecordStage(StageCls
):
540 """ example use of a Record
543 record_spec
= [('src1', 16), ('src2', 16)]
546 """ returns a Record using the specification
548 return Record(self
.record_spec
)
551 return Record(self
.record_spec
)
553 def process(self
, i
):
554 """ process the input data, returning a dictionary with key names
555 that exactly match the Record's attributes.
557 return {'src1': i
.src1
+ 1,
560 ######################################################################
562 ######################################################################
565 class ExampleAddRecordPlaceHolderStage(StageCls
):
566 """ example use of a Record, with a placeholder as the processing result
569 record_spec
= [('src1', 16), ('src2', 16)]
572 """ returns a Record using the specification
574 return Record(self
.record_spec
)
577 return Record(self
.record_spec
)
579 def process(self
, i
):
580 """ process the input data, returning a PlaceHolder class instance
581 with attributes that exactly match those of the Record.
589 # a dummy class that may have stuff assigned to instances once created
594 class ExampleAddRecordPipe(UnbufferedPipeline
):
595 """ an example of how to use the combinatorial pipeline.
599 stage
= ExampleAddRecordStage()
600 UnbufferedPipeline
.__init
__(self
, stage
)
603 def resultfn_7(o_data
, expected
, i
, o
):
604 res
= (expected
['src1'] + 1, expected
['src2'] + 1)
605 assert o_data
['src1'] == res
[0] and o_data
['src2'] == res
[1], \
606 "%d-%d data %s not match %s\n" \
607 % (i
, o
, repr(o_data
), repr(expected
))
610 class ExampleAddRecordPlaceHolderPipe(UnbufferedPipeline
):
611 """ an example of how to use the combinatorial pipeline.
615 stage
= ExampleAddRecordPlaceHolderStage()
616 UnbufferedPipeline
.__init
__(self
, stage
)
619 def resultfn_11(o_data
, expected
, i
, o
):
620 res1
= expected
.src1
+ 1
621 res2
= expected
.src2
+ 1
622 assert o_data
['src1'] == res1
and o_data
['src2'] == res2
, \
623 "%d-%d data %s not match %s\n" \
624 % (i
, o
, repr(o_data
), repr(expected
))
627 ######################################################################
629 ######################################################################
632 class Example2OpClass
:
633 """ an example of a class used to store 2 operands.
634 requires an eq function, to conform with the pipeline stage API
638 self
.op1
= Signal(16)
639 self
.op2
= Signal(16)
642 return [self
.op1
.eq(i
.op1
), self
.op2
.eq(i
.op2
)]
645 class ExampleAddClassStage(StageCls
):
646 """ an example of how to use the buffered pipeline, as a class instance
650 """ returns an instance of an Example2OpClass.
652 return Example2OpClass()
655 """ returns an output signal which will happen to contain the sum
658 return Signal(16, name
="add2_out")
660 def process(self
, i
):
661 """ process the input data (sums the values in the tuple) and returns it
666 class ExampleBufPipeAddClass(BufferedHandshake
):
667 """ an example of how to use the buffered pipeline, using a class instance
671 addstage
= ExampleAddClassStage()
672 BufferedHandshake
.__init
__(self
, addstage
)
676 """ the eq function, called by set_input, needs an incoming object
677 that conforms to the Example2OpClass.eq function requirements
678 easiest way to do that is to create a class that has the exact
679 same member layout (self.op1, self.op2) as Example2OpClass
682 def __init__(self
, op1
, op2
):
687 def resultfn_8(o_data
, expected
, i
, o
):
688 res
= expected
.op1
+ expected
.op2
# these are a TestInputAdd instance
689 assert o_data
== res
, \
690 "%d-%d data %s res %x not match %s\n" \
691 % (i
, o
, repr(o_data
), res
, repr(expected
))
696 for i
in range(num_tests
):
697 data
.append(TestInputAdd(randint(0, 1 << 16-1), randint(0, 1 << 16-1)))
701 ######################################################################
703 ######################################################################
705 class ExampleStageDelayCls(StageCls
, Elaboratable
):
706 """ an example of how to use the buffered pipeline, in a static class
710 def __init__(self
, valid_trigger
=2):
711 self
.count
= Signal(2)
712 self
.valid_trigger
= valid_trigger
715 return Signal(16, name
="example_input_signal")
718 return Signal(16, name
="example_output_signal")
722 """ data is ready to be accepted when this is true
724 return (self
.count
== 1) # | (self.count == 3)
727 def d_valid(self
, i_ready
):
728 """ data is valid at output when this is true
730 return self
.count
== self
.valid_trigger
733 def process(self
, i
):
734 """ process the input data and returns it (adds 1)
738 def elaborate(self
, platform
):
740 m
.d
.sync
+= self
.count
.eq(self
.count
+ 1)
744 class ExampleBufDelayedPipe(BufferedHandshake
):
747 stage
= ExampleStageDelayCls(valid_trigger
=2)
748 BufferedHandshake
.__init
__(self
, stage
, stage_ctl
=True)
750 def elaborate(self
, platform
):
751 m
= BufferedHandshake
.elaborate(self
, platform
)
752 m
.submodules
.stage
= self
.stage
758 for i
in range(num_tests
):
759 data
.append(1 << ((i
*3) % 15))
760 #data.append(randint(0, 1<<16-2))
761 #print (hex(data[-1]))
765 def resultfn_12(o_data
, expected
, i
, o
):
767 assert o_data
== res
, \
768 "%d-%d data %x not match %x\n" \
769 % (i
, o
, o_data
, res
)
772 ######################################################################
774 ######################################################################
776 class ExampleUnBufDelayedPipe(BufferedHandshake
):
779 stage
= ExampleStageDelayCls(valid_trigger
=3)
780 BufferedHandshake
.__init
__(self
, stage
, stage_ctl
=True)
782 def elaborate(self
, platform
):
783 m
= BufferedHandshake
.elaborate(self
, platform
)
784 m
.submodules
.stage
= self
.stage
787 ######################################################################
789 ######################################################################
792 class ExampleBufModeAdd1Pipe(SimpleHandshake
):
795 stage
= ExampleStageCls()
796 SimpleHandshake
.__init
__(self
, stage
)
799 ######################################################################
801 ######################################################################
803 class ExampleBufModeUnBufPipe(ControlBase
):
805 def elaborate(self
, platform
):
806 m
= ControlBase
.elaborate(self
, platform
)
808 pipe1
= ExampleBufModeAdd1Pipe()
809 pipe2
= ExampleBufAdd1Pipe()
811 m
.submodules
.pipe1
= pipe1
812 m
.submodules
.pipe2
= pipe2
814 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
818 ######################################################################
820 ######################################################################
823 class ExampleUnBufAdd1Pipe2(UnbufferedPipeline2
):
826 stage
= ExampleStageCls()
827 UnbufferedPipeline2
.__init__(self
, stage
)
830 ######################################################################
832 ######################################################################
834 class PassThroughTest(PassThroughHandshake
):
837 return Signal(16, name
="out")
840 stage
= PassThroughStage(self
.iospecfn
)
841 PassThroughHandshake
.__init
__(self
, stage
)
844 def resultfn_identical(o_data
, expected
, i
, o
):
846 assert o_data
== res
, \
847 "%d-%d data %x not match %x\n" \
848 % (i
, o
, o_data
, res
)
851 ######################################################################
853 ######################################################################
855 class ExamplePassAdd1Pipe(PassThroughHandshake
):
858 stage
= ExampleStageCls()
859 PassThroughHandshake
.__init
__(self
, stage
)
862 class ExampleBufPassThruPipe(ControlBase
):
864 def elaborate(self
, platform
):
865 m
= ControlBase
.elaborate(self
, platform
)
867 # XXX currently fails: any other permutation works fine.
868 # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
869 # also fails using UnbufferedPipeline as well
870 pipe1
= ExampleBufModeAdd1Pipe()
871 pipe2
= ExamplePassAdd1Pipe()
873 m
.submodules
.pipe1
= pipe1
874 m
.submodules
.pipe2
= pipe2
876 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
881 ######################################################################
883 ######################################################################
886 return Signal(16, name
="d_in")
889 class FIFOTest16(FIFOControl
):
892 stage
= PassThroughStage(iospecfn
)
893 FIFOControl
.__init
__(self
, 2, stage
)
896 ######################################################################
898 ######################################################################
900 class ExampleFIFOPassThruPipe1(ControlBase
):
902 def elaborate(self
, platform
):
903 m
= ControlBase
.elaborate(self
, platform
)
907 pipe3
= ExamplePassAdd1Pipe()
909 m
.submodules
.pipe1
= pipe1
910 m
.submodules
.pipe2
= pipe2
911 m
.submodules
.pipe3
= pipe3
913 m
.d
.comb
+= self
.connect([pipe1
, pipe2
, pipe3
])
918 ######################################################################
920 ######################################################################
922 class Example2OpRecord(RecordObject
):
924 RecordObject
.__init
__(self
)
925 self
.op1
= Signal(16)
926 self
.op2
= Signal(16)
929 class ExampleAddRecordObjectStage(StageCls
):
932 """ returns an instance of an Example2OpRecord.
934 return Example2OpRecord()
937 """ returns an output signal which will happen to contain the sum
942 def process(self
, i
):
943 """ process the input data (sums the values in the tuple) and returns it
948 class ExampleRecordHandshakeAddClass(SimpleHandshake
):
951 addstage
= ExampleAddRecordObjectStage()
952 SimpleHandshake
.__init
__(self
, stage
=addstage
)
955 ######################################################################
957 ######################################################################
959 def iospecfnrecord():
960 return Example2OpRecord()
963 class FIFOTestRecordControl(FIFOControl
):
966 stage
= PassThroughStage(iospecfnrecord
)
967 FIFOControl
.__init
__(self
, 2, stage
)
970 class ExampleFIFORecordObjectPipe(ControlBase
):
972 def elaborate(self
, platform
):
973 m
= ControlBase
.elaborate(self
, platform
)
975 pipe1
= FIFOTestRecordControl()
976 pipe2
= ExampleRecordHandshakeAddClass()
978 m
.submodules
.pipe1
= pipe1
979 m
.submodules
.pipe2
= pipe2
981 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
986 ######################################################################
988 ######################################################################
990 class FIFOTestRecordAddStageControl(FIFOControl
):
993 stage
= ExampleAddRecordObjectStage()
994 FIFOControl
.__init
__(self
, 2, stage
)
997 ######################################################################
999 ######################################################################
1001 class FIFOTestAdd16(FIFOControl
):
1004 stage
= ExampleStageCls()
1005 FIFOControl
.__init
__(self
, 2, stage
)
1008 class ExampleFIFOAdd2Pipe(ControlBase
):
1010 def elaborate(self
, platform
):
1011 m
= ControlBase
.elaborate(self
, platform
)
1013 pipe1
= FIFOTestAdd16()
1014 pipe2
= FIFOTestAdd16()
1016 m
.submodules
.pipe1
= pipe1
1017 m
.submodules
.pipe2
= pipe2
1019 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1024 ######################################################################
1026 ######################################################################
1029 return (Signal(16, name
="src1"), Signal(16, name
="src2"))
1032 class FIFOTest2x16(FIFOControl
):
1035 stage
= PassThroughStage(iospecfn2
)
1036 FIFOControl
.__init
__(self
, 2, stage
)
1039 ######################################################################
1041 ######################################################################
1043 class ExampleBufPassThruPipe2(ControlBase
):
1045 def elaborate(self
, platform
):
1046 m
= ControlBase
.elaborate(self
, platform
)
1048 # XXX currently fails: any other permutation works fine.
1049 # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
1050 # also fails using UnbufferedPipeline as well
1051 #pipe1 = ExampleUnBufAdd1Pipe()
1052 #pipe2 = ExampleBufAdd1Pipe()
1053 pipe1
= ExampleBufAdd1Pipe()
1054 pipe2
= ExamplePassAdd1Pipe()
1056 m
.submodules
.pipe1
= pipe1
1057 m
.submodules
.pipe2
= pipe2
1059 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1064 ######################################################################
1066 ######################################################################
1068 class ExampleBufPipe3(ControlBase
):
1069 """ Example of how to do delayed pipeline, where the stage signals
1070 whether it is ready.
1073 def elaborate(self
, platform
):
1074 m
= ControlBase
.elaborate(self
, platform
)
1076 pipe1
= ExampleBufDelayedPipe()
1077 pipe2
= ExampleBufPipe()
1079 m
.submodules
.pipe1
= pipe1
1080 m
.submodules
.pipe2
= pipe2
1082 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1086 ######################################################################
1087 # Test 999 - XXX FAILS
1088 # http://bugs.libre-riscv.org/show_bug.cgi?id=57
1089 ######################################################################
1092 class ExampleBufAdd1Pipe(BufferedHandshake
):
1095 stage
= ExampleStageCls()
1096 BufferedHandshake
.__init
__(self
, stage
)
1099 class ExampleUnBufAdd1Pipe(UnbufferedPipeline
):
1102 stage
= ExampleStageCls()
1103 UnbufferedPipeline
.__init
__(self
, stage
)
1106 class ExampleBufUnBufPipe(ControlBase
):
1108 def elaborate(self
, platform
):
1109 m
= ControlBase
.elaborate(self
, platform
)
1111 # XXX currently fails: any other permutation works fine.
1112 # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
1113 # also fails using UnbufferedPipeline as well
1114 #pipe1 = ExampleUnBufAdd1Pipe()
1115 #pipe2 = ExampleBufAdd1Pipe()
1116 pipe1
= ExampleBufAdd1Pipe()
1117 pipe2
= ExampleUnBufAdd1Pipe()
1119 m
.submodules
.pipe1
= pipe1
1120 m
.submodules
.pipe2
= pipe2
1122 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1127 ######################################################################
1129 ######################################################################
1131 class ExampleMaskRecord(RecordObject
):
1132 """ an example of a class used to store 2 operands.
1133 requires an eq function, to conform with the pipeline stage API
1137 RecordObject
.__init
__(self
)
1138 self
.src1
= Signal(16)
1139 self
.src2
= Signal(16)
1142 return [self
.src1
.eq(i
.src1
), self
.src2
.eq(i
.src2
)]
1145 class TestInputMask
:
1146 """ the eq function, called by set_input, needs an incoming object
1147 that conforms to the Example2OpClass.eq function requirements
1148 easiest way to do that is to create a class that has the exact
1149 same member layout (self.op1, self.op2) as Example2OpClass
1152 def __init__(self
, src1
, src2
):
1157 return "<TestInputMask %x %x" % (self
.src1
, self
.src2
)
1160 class ExampleMaskCancellable(StageCls
):
1163 """ returns an instance of an ExampleMaskRecord.
1165 return ExampleMaskRecord()
1168 """ returns the same
1170 return ExampleMaskRecord()
1172 def process(self
, i
):
1173 """ process the input data: increase op1 and op2
1175 return TestInputMask(i
.src1
+ 1, i
.src2
+ 1)
1178 class MaskCancellablePipe(MaskCancellable
):
1180 """ connects two stages together as a *single* combinatorial stage.
1183 def __init__(self
, dynamic
=False, maskwid
=16):
1184 stage1
= ExampleMaskCancellable()
1185 stage2
= ExampleMaskCancellable()
1186 combined
= StageChain([stage1
, stage2
])
1187 MaskCancellable
.__init
__(self
, combined
, maskwid
, dynamic
=dynamic
)
1190 class MaskCancellablePipe1(MaskCancellable
):
1192 """ connects a stage to a cancellable pipe with "dynamic" mode on.
1195 def __init__(self
, dynamic
=True, maskwid
=16):
1196 stage
= ExampleMaskCancellable()
1197 MaskCancellable
.__init
__(self
, stage
, maskwid
, dynamic
=dynamic
)
1200 class MaskCancellableDynamic(ControlBase
):
1202 def __init__(self
, maskwid
):
1203 self
.maskwid
= maskwid
1204 ControlBase
.__init
__(self
, None, maskwid
=maskwid
)
1206 def elaborate(self
, platform
):
1207 m
= ControlBase
.elaborate(self
, platform
)
1209 pipe1
= MaskCancellablePipe1(maskwid
=self
.maskwid
)
1210 pipe2
= MaskCancellablePipe1(maskwid
=self
.maskwid
)
1212 m
.submodules
.pipe1
= pipe1
1213 m
.submodules
.pipe2
= pipe2
1215 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1217 self
.latchmode
= Signal()
1218 m
.d
.comb
+= pipe1
.latchmode
.eq(self
.latchmode
)
1219 m
.d
.comb
+= pipe2
.latchmode
.eq(self
.latchmode
)
1220 #m.d.comb += self.latchmode.eq(1)
1225 def data_chain0(n_tests
):
1227 for i
in range(n_tests
):
1228 data
.append(TestInputMask(randint(0, 1 << 16-1),
1229 randint(0, 1 << 16-1)))
1233 def resultfn_0(o_data
, expected
, i
, o
):
1234 assert o_data
['src1'] == expected
.src1
+ 2, \
1235 "src1 %x-%x received data no match\n" \
1236 % (o_data
['src1'], expected
.src1
+ 2)
1237 assert o_data
['src2'] == expected
.src2
+ 2, \
1238 "src2 %x-%x received data no match\n" \
1239 % (o_data
['src2'], expected
.src2
+ 2)
1242 ######################################################################
1244 ######################################################################
1252 dut
= MaskCancellablePipe(maskwid
)
1253 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1254 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1255 dut
.p
.i_data
.ports() + dut
.n
.o_data
.ports()
1256 vl
= rtlil
.convert(dut
, ports
=ports
)
1257 with
open("test_maskchain0.il", "w") as f
:
1259 data
= data_chain0(maskwid
)
1260 test
= TestMask(dut
, resultfn_0
, maskwid
, data
=data
)
1261 run_simulation(dut
, [test
.send(), test
.rcv()],
1262 vcd_name
="test_maskchain0.vcd")
1268 dut
= MaskCancellableDynamic(maskwid
=maskwid
)
1269 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1270 dut
.n
.o_valid
, dut
.p
.o_ready
] # + \
1271 #dut.p.i_data.ports() + dut.n.o_data.ports()
1272 vl
= rtlil
.convert(dut
, ports
=ports
)
1273 with
open("test_maskchain0_dynamic.il", "w") as f
:
1275 data
= data_chain0(maskwid
)
1276 test
= TestMask(dut
, resultfn_0
, maskwid
, data
=data
, latching
=True)
1277 run_simulation(dut
, [test
.send(), test
.rcv()],
1278 vcd_name
="test_maskchain0_dynamic.vcd")
1283 dut
= ExampleBufPipe()
1284 run_simulation(dut
, tbench(dut
), vcd_name
="test_bufpipe.vcd")
1289 dut
= ExampleBufPipe2()
1290 run_simulation(dut
, tbench2(dut
), vcd_name
="test_bufpipe2.vcd")
1291 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1292 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1293 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1294 vl
= rtlil
.convert(dut
, ports
=ports
)
1295 with
open("test_bufpipe2.il", "w") as f
:
1301 dut
= ExampleBufPipe()
1302 test
= Test3(dut
, resultfn_3
)
1303 run_simulation(dut
, [test
.send(), test
.rcv()],
1304 vcd_name
="test_bufpipe3.vcd")
1309 dut
= ExamplePipeline()
1310 test
= Test3(dut
, resultfn_3
)
1311 run_simulation(dut
, [test
.send(), test
.rcv()],
1312 vcd_name
="test_combpipe3.vcd")
1317 dut
= ExampleBufPipe2()
1318 run_simulation(dut
, tbench4(dut
), vcd_name
="test_bufpipe4.vcd")
1323 dut
= ExampleBufPipeAdd()
1324 test
= Test5(dut
, resultfn_5
, stage_ctl
=True)
1325 run_simulation(dut
, [test
.send(), test
.rcv()],
1326 vcd_name
="test_bufpipe5.vcd")
1331 dut
= ExampleLTPipeline()
1332 test
= Test5(dut
, resultfn_6
)
1333 run_simulation(dut
, [test
.send(), test
.rcv()], vcd_name
="test_ltcomb6.vcd")
1335 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1336 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1337 list(dut
.p
.i_data
) + [dut
.n
.o_data
]
1338 vl
= rtlil
.convert(dut
, ports
=ports
)
1339 with
open("test_ltcomb_pipe.il", "w") as f
:
1345 dut
= ExampleAddRecordPipe()
1347 test
= Test5(dut
, resultfn_7
, data
=data
)
1348 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1349 dut
.n
.o_valid
, dut
.p
.o_ready
,
1350 dut
.p
.i_data
.src1
, dut
.p
.i_data
.src2
,
1351 dut
.n
.o_data
.src1
, dut
.n
.o_data
.src2
]
1352 vl
= rtlil
.convert(dut
, ports
=ports
)
1353 with
open("test_recordcomb_pipe.il", "w") as f
:
1355 run_simulation(dut
, [test
.send(), test
.rcv()],
1356 vcd_name
="test_addrecord.vcd")
1361 dut
= ExampleBufPipeAddClass()
1363 test
= Test5(dut
, resultfn_8
, data
=data
)
1364 run_simulation(dut
, [test
.send(), test
.rcv()],
1365 vcd_name
="test_bufpipe8.vcd")
1370 dut
= ExampleBufPipeChain2()
1371 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1372 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1373 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1374 vl
= rtlil
.convert(dut
, ports
=ports
)
1375 with
open("test_bufpipechain2.il", "w") as f
:
1378 data
= data_chain2()
1379 test
= Test5(dut
, resultfn_9
, data
=data
)
1380 run_simulation(dut
, [test
.send(), test
.rcv()],
1381 vcd_name
="test_bufpipechain2.vcd")
1384 @unittest.skip("broken") # FIXME
1387 dut
= ExampleLTBufferedPipeDerived()
1388 test
= Test5(dut
, resultfn_6
)
1389 run_simulation(dut
, [test
.send(), test
.rcv()],
1390 vcd_name
="test_ltbufpipe10.vcd")
1392 vl
= rtlil
.convert(dut
, ports
=ports
)
1393 with
open("test_ltbufpipe10.il", "w") as f
:
1399 dut
= ExampleAddRecordPlaceHolderPipe()
1400 data
= data_placeholder()
1401 test
= Test5(dut
, resultfn_11
, data
=data
)
1402 run_simulation(dut
, [test
.send(), test
.rcv()],
1403 vcd_name
="test_addrecord.vcd")
1408 dut
= ExampleBufDelayedPipe()
1409 data
= data_chain1()
1410 test
= Test5(dut
, resultfn_12
, data
=data
)
1411 run_simulation(dut
, [test
.send(), test
.rcv()],
1412 vcd_name
="test_bufpipe12.vcd")
1413 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1414 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1415 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1416 vl
= rtlil
.convert(dut
, ports
=ports
)
1417 with
open("test_bufpipe12.il", "w") as f
:
1423 dut
= ExampleUnBufDelayedPipe()
1424 data
= data_chain1()
1425 test
= Test5(dut
, resultfn_12
, data
=data
)
1426 run_simulation(dut
, [test
.send(), test
.rcv()],
1427 vcd_name
="test_unbufpipe13.vcd")
1428 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1429 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1430 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1431 vl
= rtlil
.convert(dut
, ports
=ports
)
1432 with
open("test_unbufpipe13.il", "w") as f
:
1438 dut
= ExampleBufModeAdd1Pipe()
1439 data
= data_chain1()
1440 test
= Test5(dut
, resultfn_12
, data
=data
)
1441 run_simulation(dut
, [test
.send(), test
.rcv()],
1442 vcd_name
="test_bufunbuf15.vcd")
1443 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1444 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1445 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1446 vl
= rtlil
.convert(dut
, ports
=ports
)
1447 with
open("test_bufunbuf15.il", "w") as f
:
1453 dut
= ExampleBufModeUnBufPipe()
1454 data
= data_chain1()
1455 test
= Test5(dut
, resultfn_9
, data
=data
)
1456 run_simulation(dut
, [test
.send(), test
.rcv()],
1457 vcd_name
="test_bufunbuf16.vcd")
1458 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1459 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1460 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1461 vl
= rtlil
.convert(dut
, ports
=ports
)
1462 with
open("test_bufunbuf16.il", "w") as f
:
1468 dut
= ExampleUnBufAdd1Pipe2()
1469 data
= data_chain1()
1470 test
= Test5(dut
, resultfn_12
, data
=data
)
1471 run_simulation(dut
, [test
.send(), test
.rcv()],
1472 vcd_name
="test_unbufpipe17.vcd")
1473 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1474 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1475 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1476 vl
= rtlil
.convert(dut
, ports
=ports
)
1477 with
open("test_unbufpipe17.il", "w") as f
:
1483 dut
= PassThroughTest()
1484 data
= data_chain1()
1485 test
= Test5(dut
, resultfn_identical
, data
=data
)
1486 run_simulation(dut
, [test
.send(), test
.rcv()],
1487 vcd_name
="test_passthru18.vcd")
1488 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1489 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1490 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1491 vl
= rtlil
.convert(dut
, ports
=ports
)
1492 with
open("test_passthru18.il", "w") as f
:
1498 dut
= ExampleBufPassThruPipe()
1499 data
= data_chain1()
1500 test
= Test5(dut
, resultfn_9
, data
=data
)
1501 run_simulation(dut
, [test
.send(), test
.rcv()],
1502 vcd_name
="test_bufpass19.vcd")
1503 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1504 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1505 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1506 vl
= rtlil
.convert(dut
, ports
=ports
)
1507 with
open("test_bufpass19.il", "w") as f
:
1514 data
= data_chain1()
1515 test
= Test5(dut
, resultfn_identical
, data
=data
)
1516 run_simulation(dut
, [test
.send(), test
.rcv()], vcd_name
="test_fifo20.vcd")
1517 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1518 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1519 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1520 vl
= rtlil
.convert(dut
, ports
=ports
)
1521 with
open("test_fifo20.il", "w") as f
:
1527 dut
= ExampleFIFOPassThruPipe1()
1528 data
= data_chain1()
1529 test
= Test5(dut
, resultfn_12
, data
=data
)
1530 run_simulation(dut
, [test
.send(), test
.rcv()],
1531 vcd_name
="test_fifopass21.vcd")
1532 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1533 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1534 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1535 vl
= rtlil
.convert(dut
, ports
=ports
)
1536 with
open("test_fifopass21.il", "w") as f
:
1542 dut
= ExampleRecordHandshakeAddClass()
1544 test
= Test5(dut
, resultfn_8
, data
=data
)
1545 run_simulation(dut
, [test
.send(), test
.rcv()],
1546 vcd_name
="test_addrecord22.vcd")
1547 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1548 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1549 [dut
.p
.i_data
.op1
, dut
.p
.i_data
.op2
] + \
1551 vl
= rtlil
.convert(dut
, ports
=ports
)
1552 with
open("test_addrecord22.il", "w") as f
:
1558 dut
= ExampleFIFORecordObjectPipe()
1560 test
= Test5(dut
, resultfn_8
, data
=data
)
1561 run_simulation(dut
, [test
.send(), test
.rcv()],
1562 vcd_name
="test_addrecord23.vcd")
1563 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1564 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1565 [dut
.p
.i_data
.op1
, dut
.p
.i_data
.op2
] + \
1567 vl
= rtlil
.convert(dut
, ports
=ports
)
1568 with
open("test_addrecord23.il", "w") as f
:
1574 dut
= FIFOTestRecordAddStageControl()
1576 test
= Test5(dut
, resultfn_8
, data
=data
)
1577 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1578 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1579 [dut
.p
.i_data
.op1
, dut
.p
.i_data
.op2
] + \
1581 vl
= rtlil
.convert(dut
, ports
=ports
)
1582 with
open("test_addrecord24.il", "w") as f
:
1584 run_simulation(dut
, [test
.send(), test
.rcv()],
1585 vcd_name
="test_addrecord24.vcd")
1590 dut
= ExampleFIFOAdd2Pipe()
1591 data
= data_chain1()
1592 test
= Test5(dut
, resultfn_9
, data
=data
)
1593 run_simulation(dut
, [test
.send(), test
.rcv()],
1594 vcd_name
="test_add2pipe25.vcd")
1595 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1596 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1597 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1598 vl
= rtlil
.convert(dut
, ports
=ports
)
1599 with
open("test_add2pipe25.il", "w") as f
:
1605 dut
= ExampleBufPassThruPipe2()
1606 data
= data_chain1()
1607 test
= Test5(dut
, resultfn_9
, data
=data
)
1608 run_simulation(dut
, [test
.send(), test
.rcv()],
1609 vcd_name
="test_bufpass997.vcd")
1610 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1611 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1612 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1613 vl
= rtlil
.convert(dut
, ports
=ports
)
1614 with
open("test_bufpass997.il", "w") as f
:
1618 @unittest.skip("buggy -- fails due to exceeding step count limit") # FIXME
1620 print("test 998 (fails, bug)")
1621 dut
= ExampleBufPipe3()
1622 data
= data_chain1()
1623 test
= Test5(dut
, resultfn_9
, data
=data
)
1624 run_simulation(dut
, [test
.send(), test
.rcv()],
1625 vcd_name
="test_bufpipe14.vcd")
1626 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1627 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1628 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1629 vl
= rtlil
.convert(dut
, ports
=ports
)
1630 with
open("test_bufpipe14.il", "w") as f
:
1635 print("test 999 (expected to fail, which is a bug)")
1636 dut
= ExampleBufUnBufPipe()
1637 data
= data_chain1()
1638 test
= Test5(dut
, resultfn_9
, data
=data
)
1639 run_simulation(dut
, [test
.send(), test
.rcv()],
1640 vcd_name
="test_bufunbuf999.vcd")
1641 ports
= [dut
.p
.i_valid
, dut
.n
.i_ready
,
1642 dut
.n
.o_valid
, dut
.p
.o_ready
] + \
1643 [dut
.p
.i_data
] + [dut
.n
.o_data
]
1644 vl
= rtlil
.convert(dut
, ports
=ports
)
1645 with
open("test_bufunbuf999.il", "w") as f
:
1649 if __name__
== '__main__':