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
42 def check_o_n_valid(dut
, val
):
43 o_n_valid
= yield dut
.n
.valid_o
44 assert o_n_valid
== val
46 def check_o_n_valid2(dut
, val
):
47 o_n_valid
= yield dut
.n
.valid_o
48 assert o_n_valid
== val
52 #yield dut.i_p_rst.eq(1)
53 yield dut
.n
.ready_i
.eq(0)
54 #yield dut.p.ready_o.eq(0)
57 #yield dut.i_p_rst.eq(0)
58 yield dut
.n
.ready_i
.eq(1)
59 yield dut
.p
.data_i
.eq(5)
60 yield dut
.p
.valid_i
.eq(1)
63 yield dut
.p
.data_i
.eq(7)
64 yield from check_o_n_valid(dut
, 0) # effects of i_p_valid delayed
66 yield from check_o_n_valid(dut
, 1) # ok *now* i_p_valid effect is felt
68 yield dut
.p
.data_i
.eq(2)
70 yield dut
.n
.ready_i
.eq(0) # begin going into "stall" (next stage says ready)
71 yield dut
.p
.data_i
.eq(9)
73 yield dut
.p
.valid_i
.eq(0)
74 yield dut
.p
.data_i
.eq(12)
76 yield dut
.p
.data_i
.eq(32)
77 yield dut
.n
.ready_i
.eq(1)
79 yield from check_o_n_valid(dut
, 1) # buffer still needs to output
81 yield from check_o_n_valid(dut
, 1) # buffer still needs to output
83 yield from check_o_n_valid(dut
, 0) # buffer outputted, *now* we're done.
88 #yield dut.p.i_rst.eq(1)
89 yield dut
.n
.ready_i
.eq(0)
90 #yield dut.p.ready_o.eq(0)
93 #yield dut.p.i_rst.eq(0)
94 yield dut
.n
.ready_i
.eq(1)
95 yield dut
.p
.data_i
.eq(5)
96 yield dut
.p
.valid_i
.eq(1)
99 yield dut
.p
.data_i
.eq(7)
100 yield from check_o_n_valid2(dut
, 0) # effects of i_p_valid delayed 2 clocks
102 yield from check_o_n_valid2(dut
, 0) # effects of i_p_valid delayed 2 clocks
104 yield dut
.p
.data_i
.eq(2)
106 yield from check_o_n_valid2(dut
, 1) # ok *now* i_p_valid effect is felt
107 yield dut
.n
.ready_i
.eq(0) # begin going into "stall" (next stage says ready)
108 yield dut
.p
.data_i
.eq(9)
110 yield dut
.p
.valid_i
.eq(0)
111 yield dut
.p
.data_i
.eq(12)
113 yield dut
.p
.data_i
.eq(32)
114 yield dut
.n
.ready_i
.eq(1)
116 yield from check_o_n_valid2(dut
, 1) # buffer still needs to output
118 yield from check_o_n_valid2(dut
, 1) # buffer still needs to output
120 yield from check_o_n_valid2(dut
, 1) # buffer still needs to output
122 yield from check_o_n_valid2(dut
, 0) # buffer outputted, *now* we're done.
129 def __init__(self
, dut
, resultfn
):
131 self
.resultfn
= resultfn
133 for i
in range(num_tests
):
134 #data.append(randint(0, 1<<16-1))
135 self
.data
.append(i
+1)
140 while self
.o
!= len(self
.data
):
141 send_range
= randint(0, 3)
142 for j
in range(randint(1,10)):
146 send
= randint(0, send_range
) != 0
147 o_p_ready
= yield self
.dut
.p
.ready_o
151 if send
and self
.i
!= len(self
.data
):
152 yield self
.dut
.p
.valid_i
.eq(1)
153 yield self
.dut
.p
.data_i
.eq(self
.data
[self
.i
])
156 yield self
.dut
.p
.valid_i
.eq(0)
160 while self
.o
!= len(self
.data
):
161 stall_range
= randint(0, 3)
162 for j
in range(randint(1,10)):
163 stall
= randint(0, stall_range
) != 0
164 yield self
.dut
.n
.ready_i
.eq(stall
)
166 o_n_valid
= yield self
.dut
.n
.valid_o
167 i_n_ready
= yield self
.dut
.n
.ready_i_test
168 if not o_n_valid
or not i_n_ready
:
170 data_o
= yield self
.dut
.n
.data_o
171 self
.resultfn(data_o
, self
.data
[self
.o
], self
.i
, self
.o
)
173 if self
.o
== len(self
.data
):
176 def resultfn_3(data_o
, expected
, i
, o
):
177 assert data_o
== expected
+ 1, \
178 "%d-%d data %x not match %x\n" \
179 % (i
, o
, data_o
, expected
)
181 def data_placeholder():
183 for i
in range(num_tests
):
185 d
.src1
= randint(0, 1<<16-1)
186 d
.src2
= randint(0, 1<<16-1)
192 for i
in range(num_tests
):
193 data
.append({'src1': randint(0, 1<<16-1),
194 'src2': randint(0, 1<<16-1)})
199 def __init__(self
, dut
, resultfn
, data
=None, stage_ctl
=False):
201 self
.resultfn
= resultfn
202 self
.stage_ctl
= stage_ctl
207 for i
in range(num_tests
):
208 self
.data
.append((randint(0, 1<<16-1), randint(0, 1<<16-1)))
213 while self
.o
!= len(self
.data
):
214 send_range
= randint(0, 3)
215 for j
in range(randint(1,10)):
219 send
= randint(0, send_range
) != 0
221 o_p_ready
= yield self
.dut
.p
.ready_o
225 if send
and self
.i
!= len(self
.data
):
226 yield self
.dut
.p
.valid_i
.eq(1)
227 for v
in self
.dut
.set_input(self
.data
[self
.i
]):
231 yield self
.dut
.p
.valid_i
.eq(0)
235 while self
.o
!= len(self
.data
):
236 stall_range
= randint(0, 3)
237 for j
in range(randint(1,10)):
238 ready
= randint(0, stall_range
) != 0
240 yield self
.dut
.n
.ready_i
.eq(ready
)
242 o_n_valid
= yield self
.dut
.n
.valid_o
243 i_n_ready
= yield self
.dut
.n
.ready_i_test
244 if not o_n_valid
or not i_n_ready
:
246 if isinstance(self
.dut
.n
.data_o
, Record
):
248 dod
= self
.dut
.n
.data_o
249 for k
, v
in dod
.fields
.items():
252 data_o
= yield self
.dut
.n
.data_o
253 self
.resultfn(data_o
, self
.data
[self
.o
], self
.i
, self
.o
)
255 if self
.o
== len(self
.data
):
259 def __init__(self
, dut
, resultfn
, maskwid
, data
=None, stage_ctl
=False,
262 self
.resultfn
= resultfn
263 self
.stage_ctl
= stage_ctl
264 self
.maskwid
= maskwid
265 self
.latching
= latching
271 for i
in range(num_tests
):
272 self
.data
.append((randint(0, 1<<16-1), randint(0, 1<<16-1)))
277 while self
.o
!= len(self
.data
):
278 send_range
= randint(0, 3)
279 for j
in range(randint(1,10)):
283 send
= randint(0, send_range
) != 0
285 o_p_ready
= yield self
.dut
.p
.ready_o
291 latchtest
= randint(0, 3) == 0
293 yield self
.dut
.p
.valid_i
.eq(0)
294 yield self
.dut
.p
.mask_i
.eq(0)
295 # wait for pipeline to flush, then invert state
298 self
.latchmode
= 1 - self
.latchmode
299 yield self
.dut
.latchmode
.eq(self
.latchmode
)
300 mode
= yield self
.dut
.latchmode
301 print ("latching", mode
)
303 if send
and self
.i
!= len(self
.data
):
304 print ("send", self
.i
, self
.data
[self
.i
])
305 yield self
.dut
.p
.valid_i
.eq(1)
306 yield self
.dut
.p
.mask_i
.eq(1<<self
.i
) # XXX TODO
307 for v
in self
.dut
.set_input(self
.data
[self
.i
]):
311 yield self
.dut
.p
.valid_i
.eq(0)
312 yield self
.dut
.p
.mask_i
.eq(0) # XXX TODO
316 while self
.o
!= len(self
.data
):
317 stall_range
= randint(0, 3)
318 for j
in range(randint(1,10)):
319 ready
= randint(0, stall_range
) != 0
321 yield self
.dut
.n
.ready_i
.eq(ready
)
323 o_n_valid
= yield self
.dut
.n
.valid_o
324 i_n_ready
= yield self
.dut
.n
.ready_i_test
325 if not o_n_valid
or not i_n_ready
:
327 if isinstance(self
.dut
.n
.data_o
, Record
):
329 dod
= self
.dut
.n
.data_o
330 for k
, v
in dod
.fields
.items():
333 data_o
= yield self
.dut
.n
.data_o
334 print ("recv", self
.o
, data_o
)
335 self
.resultfn(data_o
, self
.data
[self
.o
], self
.i
, self
.o
)
337 if self
.o
== len(self
.data
):
340 def resultfn_5(data_o
, expected
, i
, o
):
341 res
= expected
[0] + expected
[1]
342 assert data_o
== res
, \
343 "%d-%d data %x not match %s\n" \
344 % (i
, o
, data_o
, repr(expected
))
348 for i
in range(num_tests
):
349 #data.append(randint(0, 1<<16-1))
354 stall
= randint(0, 3) != 0
355 send
= randint(0, 5) != 0
356 yield dut
.n
.ready_i
.eq(stall
)
357 o_p_ready
= yield dut
.p
.ready_o
359 if send
and i
!= len(data
):
360 yield dut
.p
.valid_i
.eq(1)
361 yield dut
.p
.data_i
.eq(data
[i
])
364 yield dut
.p
.valid_i
.eq(0)
366 o_n_valid
= yield dut
.n
.valid_o
367 i_n_ready
= yield dut
.n
.ready_i_test
368 if o_n_valid
and i_n_ready
:
369 data_o
= yield dut
.n
.data_o
370 assert data_o
== data
[o
] + 2, "%d-%d data %x not match %x\n" \
371 % (i
, o
, data_o
, data
[o
])
376 ######################################################################
378 ######################################################################
380 class ExampleBufPipe2(ControlBase
):
381 """ Example of how to do chained pipeline stages.
384 def elaborate(self
, platform
):
385 m
= ControlBase
.elaborate(self
, platform
)
387 pipe1
= ExampleBufPipe()
388 pipe2
= ExampleBufPipe()
390 m
.submodules
.pipe1
= pipe1
391 m
.submodules
.pipe2
= pipe2
393 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
398 ######################################################################
400 ######################################################################
402 class ExampleBufPipeChain2(BufferedHandshake
):
403 """ connects two stages together as a *single* combinatorial stage.
406 stage1
= ExampleStageCls()
407 stage2
= ExampleStageCls()
408 combined
= StageChain([stage1
, stage2
])
409 BufferedHandshake
.__init
__(self
, combined
)
414 for i
in range(num_tests
):
415 data
.append(randint(0, 1<<16-2))
419 def resultfn_9(data_o
, expected
, i
, o
):
421 assert data_o
== res
, \
422 "%d-%d received data %x not match expected %x\n" \
423 % (i
, o
, data_o
, res
)
426 ######################################################################
428 ######################################################################
430 class SetLessThan(Elaboratable
):
431 def __init__(self
, width
, signed
):
433 self
.src1
= Signal((width
, signed
), name
="src1")
434 self
.src2
= Signal((width
, signed
), name
="src2")
435 self
.output
= Signal(width
, name
="out")
437 def elaborate(self
, platform
):
438 self
.m
.d
.comb
+= self
.output
.eq(Mux(self
.src1
< self
.src2
, 1, 0))
442 class LTStage(StageCls
):
443 """ module-based stage example
446 self
.slt
= SetLessThan(16, True)
448 def ispec(self
, name
):
449 return (Signal(16, name
="%s_sig1" % name
),
450 Signal(16, name
="%s_sig2" % name
))
452 def ospec(self
, name
):
453 return Signal(16, "%s_out" % name
)
455 def setup(self
, m
, i
):
457 m
.submodules
.slt
= self
.slt
458 m
.d
.comb
+= self
.slt
.src1
.eq(i
[0])
459 m
.d
.comb
+= self
.slt
.src2
.eq(i
[1])
460 m
.d
.comb
+= self
.o
.eq(self
.slt
.output
)
462 def process(self
, i
):
466 class LTStageDerived(SetLessThan
, StageCls
):
467 """ special version of a nmigen module where the module is also a stage
469 shows that you don't actually need to combinatorially connect
470 to the outputs, or add the module as a submodule: just return
471 the module output parameter(s) from the Stage.process() function
475 SetLessThan
.__init
__(self
, 16, True)
478 return (Signal(16), Signal(16))
483 def setup(self
, m
, i
):
484 m
.submodules
.slt
= self
485 m
.d
.comb
+= self
.src1
.eq(i
[0])
486 m
.d
.comb
+= self
.src2
.eq(i
[1])
488 def process(self
, i
):
492 class ExampleLTPipeline(UnbufferedPipeline
):
493 """ an example of how to use the unbuffered pipeline.
498 UnbufferedPipeline
.__init
__(self
, stage
)
501 class ExampleLTBufferedPipeDerived(BufferedHandshake
):
502 """ an example of how to use the buffered pipeline.
506 stage
= LTStageDerived()
507 BufferedHandshake
.__init
__(self
, stage
)
510 def resultfn_6(data_o
, expected
, i
, o
):
511 res
= 1 if expected
[0] < expected
[1] else 0
512 assert data_o
== res
, \
513 "%d-%d data %x not match %s\n" \
514 % (i
, o
, data_o
, repr(expected
))
517 ######################################################################
519 ######################################################################
521 class ExampleAddRecordStage(StageCls
):
522 """ example use of a Record
525 record_spec
= [('src1', 16), ('src2', 16)]
527 """ returns a Record using the specification
529 return Record(self
.record_spec
)
532 return Record(self
.record_spec
)
534 def process(self
, i
):
535 """ process the input data, returning a dictionary with key names
536 that exactly match the Record's attributes.
538 return {'src1': i
.src1
+ 1,
541 ######################################################################
543 ######################################################################
545 class ExampleAddRecordPlaceHolderStage(StageCls
):
546 """ example use of a Record, with a placeholder as the processing result
549 record_spec
= [('src1', 16), ('src2', 16)]
551 """ returns a Record using the specification
553 return Record(self
.record_spec
)
556 return Record(self
.record_spec
)
558 def process(self
, i
):
559 """ process the input data, returning a PlaceHolder class instance
560 with attributes that exactly match those of the Record.
568 # a dummy class that may have stuff assigned to instances once created
569 class PlaceHolder
: pass
572 class ExampleAddRecordPipe(UnbufferedPipeline
):
573 """ an example of how to use the combinatorial pipeline.
577 stage
= ExampleAddRecordStage()
578 UnbufferedPipeline
.__init
__(self
, stage
)
581 def resultfn_7(data_o
, expected
, i
, o
):
582 res
= (expected
['src1'] + 1, expected
['src2'] + 1)
583 assert data_o
['src1'] == res
[0] and data_o
['src2'] == res
[1], \
584 "%d-%d data %s not match %s\n" \
585 % (i
, o
, repr(data_o
), repr(expected
))
588 class ExampleAddRecordPlaceHolderPipe(UnbufferedPipeline
):
589 """ an example of how to use the combinatorial pipeline.
593 stage
= ExampleAddRecordPlaceHolderStage()
594 UnbufferedPipeline
.__init
__(self
, stage
)
597 def resultfn_11(data_o
, expected
, i
, o
):
598 res1
= expected
.src1
+ 1
599 res2
= expected
.src2
+ 1
600 assert data_o
['src1'] == res1
and data_o
['src2'] == res2
, \
601 "%d-%d data %s not match %s\n" \
602 % (i
, o
, repr(data_o
), repr(expected
))
605 ######################################################################
607 ######################################################################
610 class Example2OpClass
:
611 """ an example of a class used to store 2 operands.
612 requires an eq function, to conform with the pipeline stage API
616 self
.op1
= Signal(16)
617 self
.op2
= Signal(16)
620 return [self
.op1
.eq(i
.op1
), self
.op2
.eq(i
.op2
)]
623 class ExampleAddClassStage(StageCls
):
624 """ an example of how to use the buffered pipeline, as a class instance
628 """ returns an instance of an Example2OpClass.
630 return Example2OpClass()
633 """ returns an output signal which will happen to contain the sum
636 return Signal(16, name
="add2_out")
638 def process(self
, i
):
639 """ process the input data (sums the values in the tuple) and returns it
644 class ExampleBufPipeAddClass(BufferedHandshake
):
645 """ an example of how to use the buffered pipeline, using a class instance
649 addstage
= ExampleAddClassStage()
650 BufferedHandshake
.__init
__(self
, addstage
)
654 """ the eq function, called by set_input, needs an incoming object
655 that conforms to the Example2OpClass.eq function requirements
656 easiest way to do that is to create a class that has the exact
657 same member layout (self.op1, self.op2) as Example2OpClass
659 def __init__(self
, op1
, op2
):
664 def resultfn_8(data_o
, expected
, i
, o
):
665 res
= expected
.op1
+ expected
.op2
# these are a TestInputAdd instance
666 assert data_o
== res
, \
667 "%d-%d data %s res %x not match %s\n" \
668 % (i
, o
, repr(data_o
), res
, repr(expected
))
672 for i
in range(num_tests
):
673 data
.append(TestInputAdd(randint(0, 1<<16-1), randint(0, 1<<16-1)))
677 ######################################################################
679 ######################################################################
681 class ExampleStageDelayCls(StageCls
, Elaboratable
):
682 """ an example of how to use the buffered pipeline, in a static class
686 def __init__(self
, valid_trigger
=2):
687 self
.count
= Signal(2)
688 self
.valid_trigger
= valid_trigger
691 return Signal(16, name
="example_input_signal")
694 return Signal(16, name
="example_output_signal")
698 """ data is ready to be accepted when this is true
700 return (self
.count
== 1)# | (self.count == 3)
703 def d_valid(self
, ready_i
):
704 """ data is valid at output when this is true
706 return self
.count
== self
.valid_trigger
709 def process(self
, i
):
710 """ process the input data and returns it (adds 1)
714 def elaborate(self
, platform
):
716 m
.d
.sync
+= self
.count
.eq(self
.count
+ 1)
720 class ExampleBufDelayedPipe(BufferedHandshake
):
723 stage
= ExampleStageDelayCls(valid_trigger
=2)
724 BufferedHandshake
.__init
__(self
, stage
, stage_ctl
=True)
726 def elaborate(self
, platform
):
727 m
= BufferedHandshake
.elaborate(self
, platform
)
728 m
.submodules
.stage
= self
.stage
734 for i
in range(num_tests
):
735 data
.append(1<<((i
*3)%15))
736 #data.append(randint(0, 1<<16-2))
737 #print (hex(data[-1]))
741 def resultfn_12(data_o
, expected
, i
, o
):
743 assert data_o
== res
, \
744 "%d-%d data %x not match %x\n" \
745 % (i
, o
, data_o
, res
)
748 ######################################################################
750 ######################################################################
752 class ExampleUnBufDelayedPipe(BufferedHandshake
):
755 stage
= ExampleStageDelayCls(valid_trigger
=3)
756 BufferedHandshake
.__init
__(self
, stage
, stage_ctl
=True)
758 def elaborate(self
, platform
):
759 m
= BufferedHandshake
.elaborate(self
, platform
)
760 m
.submodules
.stage
= self
.stage
763 ######################################################################
765 ######################################################################
767 class ExampleBufModeAdd1Pipe(SimpleHandshake
):
770 stage
= ExampleStageCls()
771 SimpleHandshake
.__init
__(self
, stage
)
774 ######################################################################
776 ######################################################################
778 class ExampleBufModeUnBufPipe(ControlBase
):
780 def elaborate(self
, platform
):
781 m
= ControlBase
.elaborate(self
, platform
)
783 pipe1
= ExampleBufModeAdd1Pipe()
784 pipe2
= ExampleBufAdd1Pipe()
786 m
.submodules
.pipe1
= pipe1
787 m
.submodules
.pipe2
= pipe2
789 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
793 ######################################################################
795 ######################################################################
797 class ExampleUnBufAdd1Pipe2(UnbufferedPipeline2
):
800 stage
= ExampleStageCls()
801 UnbufferedPipeline2
.__init__(self
, stage
)
804 ######################################################################
806 ######################################################################
808 class PassThroughTest(PassThroughHandshake
):
811 return Signal(16, "out")
814 stage
= PassThroughStage(self
.iospecfn
)
815 PassThroughHandshake
.__init
__(self
, stage
)
817 def resultfn_identical(data_o
, expected
, i
, o
):
819 assert data_o
== res
, \
820 "%d-%d data %x not match %x\n" \
821 % (i
, o
, data_o
, res
)
824 ######################################################################
826 ######################################################################
828 class ExamplePassAdd1Pipe(PassThroughHandshake
):
831 stage
= ExampleStageCls()
832 PassThroughHandshake
.__init
__(self
, stage
)
835 class ExampleBufPassThruPipe(ControlBase
):
837 def elaborate(self
, platform
):
838 m
= ControlBase
.elaborate(self
, platform
)
840 # XXX currently fails: any other permutation works fine.
841 # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
842 # also fails using UnbufferedPipeline as well
843 pipe1
= ExampleBufModeAdd1Pipe()
844 pipe2
= ExamplePassAdd1Pipe()
846 m
.submodules
.pipe1
= pipe1
847 m
.submodules
.pipe2
= pipe2
849 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
854 ######################################################################
856 ######################################################################
859 return Signal(16, name
="d_in")
861 class FIFOTest16(FIFOControl
):
864 stage
= PassThroughStage(iospecfn
)
865 FIFOControl
.__init
__(self
, 2, stage
)
868 ######################################################################
870 ######################################################################
872 class ExampleFIFOPassThruPipe1(ControlBase
):
874 def elaborate(self
, platform
):
875 m
= ControlBase
.elaborate(self
, platform
)
879 pipe3
= ExamplePassAdd1Pipe()
881 m
.submodules
.pipe1
= pipe1
882 m
.submodules
.pipe2
= pipe2
883 m
.submodules
.pipe3
= pipe3
885 m
.d
.comb
+= self
.connect([pipe1
, pipe2
, pipe3
])
890 ######################################################################
892 ######################################################################
894 class Example2OpRecord(RecordObject
):
896 RecordObject
.__init
__(self
)
897 self
.op1
= Signal(16)
898 self
.op2
= Signal(16)
901 class ExampleAddRecordObjectStage(StageCls
):
904 """ returns an instance of an Example2OpRecord.
906 return Example2OpRecord()
909 """ returns an output signal which will happen to contain the sum
914 def process(self
, i
):
915 """ process the input data (sums the values in the tuple) and returns it
920 class ExampleRecordHandshakeAddClass(SimpleHandshake
):
923 addstage
= ExampleAddRecordObjectStage()
924 SimpleHandshake
.__init
__(self
, stage
=addstage
)
927 ######################################################################
929 ######################################################################
931 def iospecfnrecord():
932 return Example2OpRecord()
934 class FIFOTestRecordControl(FIFOControl
):
937 stage
= PassThroughStage(iospecfnrecord
)
938 FIFOControl
.__init
__(self
, 2, stage
)
941 class ExampleFIFORecordObjectPipe(ControlBase
):
943 def elaborate(self
, platform
):
944 m
= ControlBase
.elaborate(self
, platform
)
946 pipe1
= FIFOTestRecordControl()
947 pipe2
= ExampleRecordHandshakeAddClass()
949 m
.submodules
.pipe1
= pipe1
950 m
.submodules
.pipe2
= pipe2
952 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
957 ######################################################################
959 ######################################################################
961 class FIFOTestRecordAddStageControl(FIFOControl
):
964 stage
= ExampleAddRecordObjectStage()
965 FIFOControl
.__init
__(self
, 2, stage
)
969 ######################################################################
971 ######################################################################
973 class FIFOTestAdd16(FIFOControl
):
976 stage
= ExampleStageCls()
977 FIFOControl
.__init
__(self
, 2, stage
)
980 class ExampleFIFOAdd2Pipe(ControlBase
):
982 def elaborate(self
, platform
):
983 m
= ControlBase
.elaborate(self
, platform
)
985 pipe1
= FIFOTestAdd16()
986 pipe2
= FIFOTestAdd16()
988 m
.submodules
.pipe1
= pipe1
989 m
.submodules
.pipe2
= pipe2
991 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
996 ######################################################################
998 ######################################################################
1001 return (Signal(16, name
="src1"), Signal(16, name
="src2"))
1003 class FIFOTest2x16(FIFOControl
):
1006 stage
= PassThroughStage(iospecfn2
)
1007 FIFOControl
.__init
__(self
, 2, stage
)
1010 ######################################################################
1012 ######################################################################
1014 class ExampleBufPassThruPipe2(ControlBase
):
1016 def elaborate(self
, platform
):
1017 m
= ControlBase
.elaborate(self
, platform
)
1019 # XXX currently fails: any other permutation works fine.
1020 # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
1021 # also fails using UnbufferedPipeline as well
1022 #pipe1 = ExampleUnBufAdd1Pipe()
1023 #pipe2 = ExampleBufAdd1Pipe()
1024 pipe1
= ExampleBufAdd1Pipe()
1025 pipe2
= ExamplePassAdd1Pipe()
1027 m
.submodules
.pipe1
= pipe1
1028 m
.submodules
.pipe2
= pipe2
1030 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1035 ######################################################################
1037 ######################################################################
1039 class ExampleBufPipe3(ControlBase
):
1040 """ Example of how to do delayed pipeline, where the stage signals
1041 whether it is ready.
1044 def elaborate(self
, platform
):
1045 m
= ControlBase
.elaborate(self
, platform
)
1047 pipe1
= ExampleBufDelayedPipe()
1048 pipe2
= ExampleBufPipe()
1050 m
.submodules
.pipe1
= pipe1
1051 m
.submodules
.pipe2
= pipe2
1053 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1057 ######################################################################
1058 # Test 999 - XXX FAILS
1059 # http://bugs.libre-riscv.org/show_bug.cgi?id=57
1060 ######################################################################
1062 class ExampleBufAdd1Pipe(BufferedHandshake
):
1065 stage
= ExampleStageCls()
1066 BufferedHandshake
.__init
__(self
, stage
)
1069 class ExampleUnBufAdd1Pipe(UnbufferedPipeline
):
1072 stage
= ExampleStageCls()
1073 UnbufferedPipeline
.__init
__(self
, stage
)
1076 class ExampleBufUnBufPipe(ControlBase
):
1078 def elaborate(self
, platform
):
1079 m
= ControlBase
.elaborate(self
, platform
)
1081 # XXX currently fails: any other permutation works fine.
1082 # p1=u,p2=b ok p1=u,p2=u ok p1=b,p2=b ok
1083 # also fails using UnbufferedPipeline as well
1084 #pipe1 = ExampleUnBufAdd1Pipe()
1085 #pipe2 = ExampleBufAdd1Pipe()
1086 pipe1
= ExampleBufAdd1Pipe()
1087 pipe2
= ExampleUnBufAdd1Pipe()
1089 m
.submodules
.pipe1
= pipe1
1090 m
.submodules
.pipe2
= pipe2
1092 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1097 ######################################################################
1099 ######################################################################
1101 class ExampleMaskRecord(RecordObject
):
1102 """ an example of a class used to store 2 operands.
1103 requires an eq function, to conform with the pipeline stage API
1107 RecordObject
.__init
__(self
)
1108 self
.src1
= Signal(16)
1109 self
.src2
= Signal(16)
1112 return [self
.src1
.eq(i
.src1
), self
.src2
.eq(i
.src2
)]
1115 class TestInputMask
:
1116 """ the eq function, called by set_input, needs an incoming object
1117 that conforms to the Example2OpClass.eq function requirements
1118 easiest way to do that is to create a class that has the exact
1119 same member layout (self.op1, self.op2) as Example2OpClass
1121 def __init__(self
, src1
, src2
):
1126 return "<TestInputMask %x %x" % (self
.src1
, self
.src2
)
1128 class ExampleMaskCancellable(StageCls
):
1131 """ returns an instance of an ExampleMaskRecord.
1133 return ExampleMaskRecord()
1136 """ returns the same
1138 return ExampleMaskRecord()
1140 def process(self
, i
):
1141 """ process the input data: increase op1 and op2
1143 return TestInputMask(i
.src1
+ 1, i
.src2
+ 1)
1146 class MaskCancellablePipe(MaskCancellable
):
1148 """ connects two stages together as a *single* combinatorial stage.
1150 def __init__(self
, dynamic
=False, maskwid
=16):
1151 stage1
= ExampleMaskCancellable()
1152 stage2
= ExampleMaskCancellable()
1153 combined
= StageChain([stage1
, stage2
])
1154 MaskCancellable
.__init
__(self
, combined
, maskwid
, dynamic
=dynamic
)
1157 class MaskCancellablePipe1(MaskCancellable
):
1159 """ connects a stage to a cancellable pipe with "dynamic" mode on.
1161 def __init__(self
, dynamic
=True, maskwid
=16):
1162 stage
= ExampleMaskCancellable()
1163 MaskCancellable
.__init
__(self
, stage
, maskwid
, dynamic
=dynamic
)
1166 class MaskCancellableDynamic(ControlBase
):
1168 def __init__(self
, maskwid
):
1169 self
.maskwid
= maskwid
1170 ControlBase
.__init
__(self
, None, maskwid
=maskwid
)
1172 def elaborate(self
, platform
):
1173 m
= ControlBase
.elaborate(self
, platform
)
1175 pipe1
= MaskCancellablePipe1(maskwid
=self
.maskwid
)
1176 pipe2
= MaskCancellablePipe1(maskwid
=self
.maskwid
)
1178 m
.submodules
.pipe1
= pipe1
1179 m
.submodules
.pipe2
= pipe2
1181 m
.d
.comb
+= self
.connect([pipe1
, pipe2
])
1183 self
.latchmode
= Signal()
1184 m
.d
.comb
+= pipe1
.latchmode
.eq(self
.latchmode
)
1185 m
.d
.comb
+= pipe2
.latchmode
.eq(self
.latchmode
)
1186 #m.d.comb += self.latchmode.eq(1)
1191 def data_chain0(n_tests
):
1193 for i
in range(n_tests
):
1194 data
.append(TestInputMask(randint(0, 1<<16-1),
1195 randint(0, 1<<16-1)))
1199 def resultfn_0(data_o
, expected
, i
, o
):
1200 assert data_o
['src1'] == expected
.src1
+ 2, \
1201 "src1 %x-%x received data no match\n" \
1202 % (data_o
['src1'], expected
.src1
+ 2)
1203 assert data_o
['src2'] == expected
.src2
+ 2, \
1204 "src2 %x-%x received data no match\n" \
1205 % (data_o
['src2'] , expected
.src2
+ 2)
1208 ######################################################################
1210 ######################################################################
1217 dut
= MaskCancellablePipe(maskwid
)
1218 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1219 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1220 dut
.p
.data_i
.ports() + dut
.n
.data_o
.ports()
1221 vl
= rtlil
.convert(dut
, ports
=ports
)
1222 with
open("test_maskchain0.il", "w") as f
:
1224 data
= data_chain0(maskwid
)
1225 test
= TestMask(dut
, resultfn_0
, maskwid
, data
=data
)
1226 run_simulation(dut
, [test
.send
, test
.rcv
],
1227 vcd_name
="test_maskchain0.vcd")
1232 dut
= MaskCancellableDynamic(maskwid
=maskwid
)
1233 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1234 dut
.n
.valid_o
, dut
.p
.ready_o
] #+ \
1235 #dut.p.data_i.ports() + dut.n.data_o.ports()
1236 vl
= rtlil
.convert(dut
, ports
=ports
)
1237 with
open("test_maskchain0_dynamic.il", "w") as f
:
1239 data
= data_chain0(maskwid
)
1240 test
= TestMask(dut
, resultfn_0
, maskwid
, data
=data
, latching
=True)
1241 run_simulation(dut
, [test
.send
, test
.rcv
],
1242 vcd_name
="test_maskchain0_dynamic.vcd")
1246 dut
= ExampleBufPipe()
1247 run_simulation(dut
, tbench(dut
), vcd_name
="test_bufpipe.vcd")
1251 dut
= ExampleBufPipe2()
1252 run_simulation(dut
, tbench2(dut
), vcd_name
="test_bufpipe2.vcd")
1253 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1254 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1255 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1256 vl
= rtlil
.convert(dut
, ports
=ports
)
1257 with
open("test_bufpipe2.il", "w") as f
:
1262 dut
= ExampleBufPipe()
1263 test
= Test3(dut
, resultfn_3
)
1264 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufpipe3.vcd")
1268 dut
= ExamplePipeline()
1269 test
= Test3(dut
, resultfn_3
)
1270 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_combpipe3.vcd")
1274 dut
= ExampleBufPipe2()
1275 run_simulation(dut
, tbench4(dut
), vcd_name
="test_bufpipe4.vcd")
1279 dut
= ExampleBufPipeAdd()
1280 test
= Test5(dut
, resultfn_5
, stage_ctl
=True)
1281 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufpipe5.vcd")
1285 dut
= ExampleLTPipeline()
1286 test
= Test5(dut
, resultfn_6
)
1287 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_ltcomb6.vcd")
1289 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1290 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1291 list(dut
.p
.data_i
) + [dut
.n
.data_o
]
1292 vl
= rtlil
.convert(dut
, ports
=ports
)
1293 with
open("test_ltcomb_pipe.il", "w") as f
:
1298 dut
= ExampleAddRecordPipe()
1300 test
= Test5(dut
, resultfn_7
, data
=data
)
1301 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1302 dut
.n
.valid_o
, dut
.p
.ready_o
,
1303 dut
.p
.data_i
.src1
, dut
.p
.data_i
.src2
,
1304 dut
.n
.data_o
.src1
, dut
.n
.data_o
.src2
]
1305 vl
= rtlil
.convert(dut
, ports
=ports
)
1306 with
open("test_recordcomb_pipe.il", "w") as f
:
1308 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_addrecord.vcd")
1312 dut
= ExampleBufPipeAddClass()
1314 test
= Test5(dut
, resultfn_8
, data
=data
)
1315 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufpipe8.vcd")
1319 dut
= ExampleBufPipeChain2()
1320 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1321 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1322 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1323 vl
= rtlil
.convert(dut
, ports
=ports
)
1324 with
open("test_bufpipechain2.il", "w") as f
:
1327 data
= data_chain2()
1328 test
= Test5(dut
, resultfn_9
, data
=data
)
1329 run_simulation(dut
, [test
.send
, test
.rcv
],
1330 vcd_name
="test_bufpipechain2.vcd")
1334 dut
= ExampleLTBufferedPipeDerived()
1335 test
= Test5(dut
, resultfn_6
)
1336 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_ltbufpipe10.vcd")
1338 vl
= rtlil
.convert(dut
, ports
=ports
)
1339 with
open("test_ltbufpipe10.il", "w") as f
:
1344 dut
= ExampleAddRecordPlaceHolderPipe()
1345 data
=data_placeholder()
1346 test
= Test5(dut
, resultfn_11
, data
=data
)
1347 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_addrecord.vcd")
1352 dut
= ExampleBufDelayedPipe()
1353 data
= data_chain1()
1354 test
= Test5(dut
, resultfn_12
, data
=data
)
1355 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufpipe12.vcd")
1356 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1357 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1358 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1359 vl
= rtlil
.convert(dut
, ports
=ports
)
1360 with
open("test_bufpipe12.il", "w") as f
:
1365 dut
= ExampleUnBufDelayedPipe()
1366 data
= data_chain1()
1367 test
= Test5(dut
, resultfn_12
, data
=data
)
1368 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_unbufpipe13.vcd")
1369 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1370 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1371 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1372 vl
= rtlil
.convert(dut
, ports
=ports
)
1373 with
open("test_unbufpipe13.il", "w") as f
:
1378 dut
= ExampleBufModeAdd1Pipe()
1379 data
= data_chain1()
1380 test
= Test5(dut
, resultfn_12
, data
=data
)
1381 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufunbuf15.vcd")
1382 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1383 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1384 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1385 vl
= rtlil
.convert(dut
, ports
=ports
)
1386 with
open("test_bufunbuf15.il", "w") as f
:
1391 dut
= ExampleBufModeUnBufPipe()
1392 data
= data_chain1()
1393 test
= Test5(dut
, resultfn_9
, data
=data
)
1394 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufunbuf16.vcd")
1395 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1396 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1397 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1398 vl
= rtlil
.convert(dut
, ports
=ports
)
1399 with
open("test_bufunbuf16.il", "w") as f
:
1404 dut
= ExampleUnBufAdd1Pipe2()
1405 data
= data_chain1()
1406 test
= Test5(dut
, resultfn_12
, data
=data
)
1407 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_unbufpipe17.vcd")
1408 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1409 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1410 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1411 vl
= rtlil
.convert(dut
, ports
=ports
)
1412 with
open("test_unbufpipe17.il", "w") as f
:
1417 dut
= PassThroughTest()
1418 data
= data_chain1()
1419 test
= Test5(dut
, resultfn_identical
, data
=data
)
1420 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_passthru18.vcd")
1421 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1422 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1423 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1424 vl
= rtlil
.convert(dut
, ports
=ports
)
1425 with
open("test_passthru18.il", "w") as f
:
1430 dut
= ExampleBufPassThruPipe()
1431 data
= data_chain1()
1432 test
= Test5(dut
, resultfn_9
, data
=data
)
1433 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufpass19.vcd")
1434 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1435 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1436 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1437 vl
= rtlil
.convert(dut
, ports
=ports
)
1438 with
open("test_bufpass19.il", "w") as f
:
1444 data
= data_chain1()
1445 test
= Test5(dut
, resultfn_identical
, data
=data
)
1446 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_fifo20.vcd")
1447 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1448 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1449 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1450 vl
= rtlil
.convert(dut
, ports
=ports
)
1451 with
open("test_fifo20.il", "w") as f
:
1456 dut
= ExampleFIFOPassThruPipe1()
1457 data
= data_chain1()
1458 test
= Test5(dut
, resultfn_12
, data
=data
)
1459 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_fifopass21.vcd")
1460 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1461 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1462 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1463 vl
= rtlil
.convert(dut
, ports
=ports
)
1464 with
open("test_fifopass21.il", "w") as f
:
1469 dut
= ExampleRecordHandshakeAddClass()
1471 test
= Test5(dut
, resultfn_8
, data
=data
)
1472 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_addrecord22.vcd")
1473 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1474 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1475 [dut
.p
.data_i
.op1
, dut
.p
.data_i
.op2
] + \
1477 vl
= rtlil
.convert(dut
, ports
=ports
)
1478 with
open("test_addrecord22.il", "w") as f
:
1483 dut
= ExampleFIFORecordObjectPipe()
1485 test
= Test5(dut
, resultfn_8
, data
=data
)
1486 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_addrecord23.vcd")
1487 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1488 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1489 [dut
.p
.data_i
.op1
, dut
.p
.data_i
.op2
] + \
1491 vl
= rtlil
.convert(dut
, ports
=ports
)
1492 with
open("test_addrecord23.il", "w") as f
:
1497 dut
= FIFOTestRecordAddStageControl()
1499 test
= Test5(dut
, resultfn_8
, data
=data
)
1500 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1501 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1502 [dut
.p
.data_i
.op1
, dut
.p
.data_i
.op2
] + \
1504 vl
= rtlil
.convert(dut
, ports
=ports
)
1505 with
open("test_addrecord24.il", "w") as f
:
1507 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_addrecord24.vcd")
1511 dut
= ExampleFIFOAdd2Pipe()
1512 data
= data_chain1()
1513 test
= Test5(dut
, resultfn_9
, data
=data
)
1514 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_add2pipe25.vcd")
1515 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1516 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1517 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1518 vl
= rtlil
.convert(dut
, ports
=ports
)
1519 with
open("test_add2pipe25.il", "w") as f
:
1524 dut
= ExampleBufPassThruPipe2()
1525 data
= data_chain1()
1526 test
= Test5(dut
, resultfn_9
, data
=data
)
1527 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufpass997.vcd")
1528 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1529 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1530 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1531 vl
= rtlil
.convert(dut
, ports
=ports
)
1532 with
open("test_bufpass997.il", "w") as f
:
1536 print ("test 998 (fails, bug)")
1537 dut
= ExampleBufPipe3()
1538 data
= data_chain1()
1539 test
= Test5(dut
, resultfn_9
, data
=data
)
1540 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufpipe14.vcd")
1541 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1542 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1543 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1544 vl
= rtlil
.convert(dut
, ports
=ports
)
1545 with
open("test_bufpipe14.il", "w") as f
:
1549 print ("test 999 (expected to fail, which is a bug)")
1550 dut
= ExampleBufUnBufPipe()
1551 data
= data_chain1()
1552 test
= Test5(dut
, resultfn_9
, data
=data
)
1553 run_simulation(dut
, [test
.send
, test
.rcv
], vcd_name
="test_bufunbuf999.vcd")
1554 ports
= [dut
.p
.valid_i
, dut
.n
.ready_i
,
1555 dut
.n
.valid_o
, dut
.p
.ready_o
] + \
1556 [dut
.p
.data_i
] + [dut
.n
.data_o
]
1557 vl
= rtlil
.convert(dut
, ports
=ports
)
1558 with
open("test_bufunbuf999.il", "w") as f
:
1561 if __name__
== '__main__':