add placeholder-variant pipeline stage of Record
[ieee754fpu.git] / src / add / test_buf_pipe.py
1 from nmigen import Module, Signal, Mux
2 from nmigen.hdl.rec import Record
3 from nmigen.compat.sim import run_simulation
4 from nmigen.cli import verilog, rtlil
5
6 from example_buf_pipe import ExampleBufPipe, ExampleBufPipeAdd
7 from example_buf_pipe import ExamplePipeline, UnbufferedPipeline
8 from example_buf_pipe import ExampleStageCls
9 from example_buf_pipe import PrevControl, NextControl, BufferedPipeline
10 from example_buf_pipe import StageChain
11
12 from random import randint
13
14
15 def check_o_n_valid(dut, val):
16 o_n_valid = yield dut.n.o_valid
17 assert o_n_valid == val
18
19
20 def testbench(dut):
21 #yield dut.i_p_rst.eq(1)
22 yield dut.n.i_ready.eq(0)
23 yield dut.p.o_ready.eq(0)
24 yield
25 yield
26 #yield dut.i_p_rst.eq(0)
27 yield dut.n.i_ready.eq(1)
28 yield dut.p.i_data.eq(5)
29 yield dut.p.i_valid.eq(1)
30 yield
31
32 yield dut.p.i_data.eq(7)
33 yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed
34 yield
35 yield from check_o_n_valid(dut, 1) # ok *now* i_p_valid effect is felt
36
37 yield dut.p.i_data.eq(2)
38 yield
39 yield dut.n.i_ready.eq(0) # begin going into "stall" (next stage says ready)
40 yield dut.p.i_data.eq(9)
41 yield
42 yield dut.p.i_valid.eq(0)
43 yield dut.p.i_data.eq(12)
44 yield
45 yield dut.p.i_data.eq(32)
46 yield dut.n.i_ready.eq(1)
47 yield
48 yield from check_o_n_valid(dut, 1) # buffer still needs to output
49 yield
50 yield from check_o_n_valid(dut, 1) # buffer still needs to output
51 yield
52 yield from check_o_n_valid(dut, 0) # buffer outputted, *now* we're done.
53 yield
54
55
56 def testbench2(dut):
57 #yield dut.p.i_rst.eq(1)
58 yield dut.n.i_ready.eq(0)
59 #yield dut.p.o_ready.eq(0)
60 yield
61 yield
62 #yield dut.p.i_rst.eq(0)
63 yield dut.n.i_ready.eq(1)
64 yield dut.p.i_data.eq(5)
65 yield dut.p.i_valid.eq(1)
66 yield
67
68 yield dut.p.i_data.eq(7)
69 yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed 2 clocks
70 yield
71 yield from check_o_n_valid(dut, 0) # effects of i_p_valid delayed 2 clocks
72
73 yield dut.p.i_data.eq(2)
74 yield
75 yield from check_o_n_valid(dut, 1) # ok *now* i_p_valid effect is felt
76 yield dut.n.i_ready.eq(0) # begin going into "stall" (next stage says ready)
77 yield dut.p.i_data.eq(9)
78 yield
79 yield dut.p.i_valid.eq(0)
80 yield dut.p.i_data.eq(12)
81 yield
82 yield dut.p.i_data.eq(32)
83 yield dut.n.i_ready.eq(1)
84 yield
85 yield from check_o_n_valid(dut, 1) # buffer still needs to output
86 yield
87 yield from check_o_n_valid(dut, 1) # buffer still needs to output
88 yield
89 yield from check_o_n_valid(dut, 1) # buffer still needs to output
90 yield
91 yield from check_o_n_valid(dut, 0) # buffer outputted, *now* we're done.
92 yield
93 yield
94 yield
95
96
97 class Test3:
98 def __init__(self, dut, resultfn):
99 self.dut = dut
100 self.resultfn = resultfn
101 self.data = []
102 for i in range(num_tests):
103 #data.append(randint(0, 1<<16-1))
104 self.data.append(i+1)
105 self.i = 0
106 self.o = 0
107
108 def send(self):
109 while self.o != len(self.data):
110 send_range = randint(0, 3)
111 for j in range(randint(1,10)):
112 if send_range == 0:
113 send = True
114 else:
115 send = randint(0, send_range) != 0
116 o_p_ready = yield self.dut.p.o_ready
117 if not o_p_ready:
118 yield
119 continue
120 if send and self.i != len(self.data):
121 yield self.dut.p.i_valid.eq(1)
122 yield self.dut.p.i_data.eq(self.data[self.i])
123 self.i += 1
124 else:
125 yield self.dut.p.i_valid.eq(0)
126 yield
127
128 def rcv(self):
129 while self.o != len(self.data):
130 stall_range = randint(0, 3)
131 for j in range(randint(1,10)):
132 stall = randint(0, stall_range) != 0
133 yield self.dut.n.i_ready.eq(stall)
134 yield
135 o_n_valid = yield self.dut.n.o_valid
136 i_n_ready = yield self.dut.n.i_ready
137 if not o_n_valid or not i_n_ready:
138 continue
139 o_data = yield self.dut.n.o_data
140 self.resultfn(o_data, self.data[self.o], self.i, self.o)
141 self.o += 1
142 if self.o == len(self.data):
143 break
144
145 def test3_resultfn(o_data, expected, i, o):
146 assert o_data == expected + 1, \
147 "%d-%d data %x not match %x\n" \
148 % (i, o, o_data, expected)
149
150 def data_placeholder():
151 data = []
152 for i in range(num_tests):
153 d = PlaceHolder()
154 d.src1 = randint(0, 1<<16-1)
155 d.src2 = randint(0, 1<<16-1)
156 data.append(d)
157 return data
158
159 def data_dict():
160 data = []
161 for i in range(num_tests):
162 data.append({'src1': randint(0, 1<<16-1),
163 'src2': randint(0, 1<<16-1)})
164 return data
165
166
167 class Test5:
168 def __init__(self, dut, resultfn, data=None):
169 self.dut = dut
170 self.resultfn = resultfn
171 if data:
172 self.data = data
173 else:
174 self.data = []
175 for i in range(num_tests):
176 self.data.append((randint(0, 1<<16-1), randint(0, 1<<16-1)))
177 self.i = 0
178 self.o = 0
179
180 def send(self):
181 while self.o != len(self.data):
182 send_range = randint(0, 3)
183 for j in range(randint(1,10)):
184 if send_range == 0:
185 send = True
186 else:
187 send = randint(0, send_range) != 0
188 o_p_ready = yield self.dut.p.o_ready
189 if not o_p_ready:
190 yield
191 continue
192 if send and self.i != len(self.data):
193 yield self.dut.p.i_valid.eq(1)
194 for v in self.dut.set_input(self.data[self.i]):
195 yield v
196 self.i += 1
197 else:
198 yield self.dut.p.i_valid.eq(0)
199 yield
200
201 def rcv(self):
202 while self.o != len(self.data):
203 stall_range = randint(0, 3)
204 for j in range(randint(1,10)):
205 stall = randint(0, stall_range) != 0
206 yield self.dut.n.i_ready.eq(stall)
207 yield
208 o_n_valid = yield self.dut.n.o_valid
209 i_n_ready = yield self.dut.n.i_ready
210 if not o_n_valid or not i_n_ready:
211 continue
212 if isinstance(self.dut.n.o_data, Record):
213 o_data = {}
214 dod = self.dut.n.o_data
215 for k, v in dod.fields.items():
216 o_data[k] = yield v
217 else:
218 o_data = yield self.dut.n.o_data
219 self.resultfn(o_data, self.data[self.o], self.i, self.o)
220 self.o += 1
221 if self.o == len(self.data):
222 break
223
224 def test5_resultfn(o_data, expected, i, o):
225 res = expected[0] + expected[1]
226 assert o_data == res, \
227 "%d-%d data %x not match %s\n" \
228 % (i, o, o_data, repr(expected))
229
230 def testbench4(dut):
231 data = []
232 for i in range(num_tests):
233 #data.append(randint(0, 1<<16-1))
234 data.append(i+1)
235 i = 0
236 o = 0
237 while True:
238 stall = randint(0, 3) != 0
239 send = randint(0, 5) != 0
240 yield dut.n.i_ready.eq(stall)
241 o_p_ready = yield dut.p.o_ready
242 if o_p_ready:
243 if send and i != len(data):
244 yield dut.p.i_valid.eq(1)
245 yield dut.p.i_data.eq(data[i])
246 i += 1
247 else:
248 yield dut.p.i_valid.eq(0)
249 yield
250 o_n_valid = yield dut.n.o_valid
251 i_n_ready = yield dut.n.i_ready
252 if o_n_valid and i_n_ready:
253 o_data = yield dut.n.o_data
254 assert o_data == data[o] + 2, "%d-%d data %x not match %x\n" \
255 % (i, o, o_data, data[o])
256 o += 1
257 if o == len(data):
258 break
259
260
261 class ExampleBufPipe2:
262 """
263 connect these: ------|---------------|
264 v v
265 i_p_valid >>in pipe1 o_n_valid out>> i_p_valid >>in pipe2
266 o_p_ready <<out pipe1 i_n_ready <<in o_p_ready <<out pipe2
267 p_i_data >>in pipe1 p_i_data out>> n_o_data >>in pipe2
268 """
269 def __init__(self):
270 self.pipe1 = ExampleBufPipe()
271 self.pipe2 = ExampleBufPipe()
272
273 # input
274 self.p = PrevControl()
275 self.p.i_data = Signal(32) # >>in - comes in from the PREVIOUS stage
276
277 # output
278 self.n = NextControl()
279 self.n.o_data = Signal(32) # out>> - goes out to the NEXT stage
280
281 def elaborate(self, platform):
282 m = Module()
283 m.submodules.pipe1 = self.pipe1
284 m.submodules.pipe2 = self.pipe2
285
286 # connect inter-pipe input/output valid/ready/data
287 m.d.comb += self.pipe1.connect_to_next(self.pipe2)
288
289 # inputs/outputs to the module: pipe1 connections here (LHS)
290 m.d.comb += self.pipe1.connect_in(self)
291
292 # now pipe2 connections (RHS)
293 m.d.comb += self.pipe2.connect_out(self)
294
295 return m
296
297
298 class ExampleBufPipeChain2(BufferedPipeline):
299 """ connects two stages together as a *single* combinatorial stage.
300 """
301 def __init__(self):
302 stage1 = ExampleStageCls()
303 stage2 = ExampleStageCls()
304 combined = StageChain([stage1, stage2])
305 BufferedPipeline.__init__(self, combined)
306
307
308 def data_chain2():
309 data = []
310 for i in range(num_tests):
311 data.append(randint(0, 1<<16-2))
312 return data
313
314
315 def test9_resultfn(o_data, expected, i, o):
316 res = expected + 2
317 assert o_data == res, \
318 "%d-%d data %x not match %s\n" \
319 % (i, o, o_data, repr(expected))
320
321
322 class SetLessThan:
323 def __init__(self, width, signed):
324 self.src1 = Signal((width, signed))
325 self.src2 = Signal((width, signed))
326 self.output = Signal(width)
327
328 def elaborate(self, platform):
329 m = Module()
330 m.d.comb += self.output.eq(Mux(self.src1 < self.src2, 1, 0))
331 return m
332
333
334 class LTStage:
335 def __init__(self):
336 self.slt = SetLessThan(16, True)
337
338 def ispec(self):
339 return (Signal(16), Signal(16))
340
341 def ospec(self):
342 return Signal(16)
343
344 def setup(self, m, i):
345 self.o = Signal(16)
346 m.submodules.slt = self.slt
347 m.d.comb += self.slt.src1.eq(i[0])
348 m.d.comb += self.slt.src2.eq(i[1])
349 m.d.comb += self.o.eq(self.slt.output)
350
351 def process(self, i):
352 return self.o
353
354
355 class LTStageDerived(SetLessThan):
356
357 def __init__(self):
358 SetLessThan.__init__(self, 16, True)
359
360 def ispec(self):
361 return (Signal(16), Signal(16))
362
363 def ospec(self):
364 return Signal(16)
365
366 def setup(self, m, i):
367 m.submodules.slt = self
368 m.d.comb += self.src1.eq(i[0])
369 m.d.comb += self.src2.eq(i[1])
370
371 def process(self, i):
372 return self.output
373
374
375 class ExampleLTPipeline(UnbufferedPipeline):
376 """ an example of how to use the combinatorial pipeline.
377 """
378
379 def __init__(self):
380 stage = LTStage()
381 UnbufferedPipeline.__init__(self, stage)
382
383
384 class ExampleLTBufferedPipeDerived(BufferedPipeline):
385 """ an example of how to use the combinatorial pipeline.
386 """
387
388 def __init__(self):
389 stage = LTStageDerived()
390 BufferedPipeline.__init__(self, stage)
391
392
393 def test6_resultfn(o_data, expected, i, o):
394 res = 1 if expected[0] < expected[1] else 0
395 assert o_data == res, \
396 "%d-%d data %x not match %s\n" \
397 % (i, o, o_data, repr(expected))
398
399
400 class ExampleAddRecordStage:
401 """ example use of a Record
402 """
403
404 record_spec = [('src1', 16), ('src2', 16)]
405 def ispec(self):
406 """ returns a tuple of input signals which will be the incoming data
407 """
408 return Record(self.record_spec)
409
410 def ospec(self):
411 return Record(self.record_spec)
412
413 def process(self, i):
414 """ process the input data (sums the values in the tuple) and returns it
415 """
416 return {'src1': i.src1 + 1,
417 'src2': i.src2 + 1}
418
419
420 class ExampleAddRecordPlaceHolderStage:
421 """ example use of a Record, with a placeholder as the processing result
422 """
423
424 record_spec = [('src1', 16), ('src2', 16)]
425 def ispec(self):
426 """ returns a tuple of input signals which will be the incoming data
427 """
428 return Record(self.record_spec)
429
430 def ospec(self):
431 return Record(self.record_spec)
432
433 def process(self, i):
434 """ process the input data (sums the values in the tuple) and returns it
435 """
436 o = PlaceHolder()
437 o.src1 = i.src1 + 1
438 o.src2 = i.src2 + 1
439 return o
440
441 class PlaceHolder: pass
442
443
444 class ExampleAddRecordPipe(UnbufferedPipeline):
445 """ an example of how to use the combinatorial pipeline.
446 """
447
448 def __init__(self):
449 stage = ExampleAddRecordStage()
450 UnbufferedPipeline.__init__(self, stage)
451
452
453 def test7_resultfn(o_data, expected, i, o):
454 res = (expected['src1'] + 1, expected['src2'] + 1)
455 assert o_data['src1'] == res[0] and o_data['src2'] == res[1], \
456 "%d-%d data %s not match %s\n" \
457 % (i, o, repr(o_data), repr(expected))
458
459
460 class ExampleAddRecordPlaceHolderPipe(UnbufferedPipeline):
461 """ an example of how to use the combinatorial pipeline.
462 """
463
464 def __init__(self):
465 stage = ExampleAddRecordPlaceHolderStage()
466 UnbufferedPipeline.__init__(self, stage)
467
468
469 def test11_resultfn(o_data, expected, i, o):
470 res1 = expected.src1 + 1
471 res2 = expected.src2 + 1
472 assert o_data['src1'] == res1 and o_data['src2'] == res2, \
473 "%d-%d data %s not match %s\n" \
474 % (i, o, repr(o_data), repr(expected))
475
476
477 class Example2OpClass:
478 """ an example of a class used to store 2 operands.
479 requires an eq function, to conform with the pipeline stage API
480 """
481
482 def __init__(self):
483 self.op1 = Signal(16)
484 self.op2 = Signal(16)
485
486 def eq(self, i):
487 return [self.op1.eq(i.op1), self.op2.eq(i.op2)]
488
489
490 class ExampleAddClassStage:
491 """ an example of how to use the buffered pipeline, as a class instance
492 """
493
494 def ispec(self):
495 """ returns an instance of an Example2OpClass.
496 """
497 return Example2OpClass()
498
499 def ospec(self):
500 """ returns an output signal which will happen to contain the sum
501 of the two inputs
502 """
503 return Signal(16)
504
505 def process(self, i):
506 """ process the input data (sums the values in the tuple) and returns it
507 """
508 return i.op1 + i.op2
509
510
511 class ExampleBufPipeAddClass(BufferedPipeline):
512 """ an example of how to use the buffered pipeline, using a class instance
513 """
514
515 def __init__(self):
516 addstage = ExampleAddClassStage()
517 BufferedPipeline.__init__(self, addstage)
518
519
520 class TestInputAdd:
521 """ the eq function, called by set_input, needs an incoming object
522 that conforms to the Example2OpClass.eq function requirements
523 easiest way to do that is to create a class that has the exact
524 same member layout (self.op1, self.op2) as Example2OpClass
525 """
526 def __init__(self, op1, op2):
527 self.op1 = op1
528 self.op2 = op2
529
530
531 def test8_resultfn(o_data, expected, i, o):
532 res = expected.op1 + expected.op2 # these are a TestInputAdd instance
533 assert o_data == res, \
534 "%d-%d data %x not match %s\n" \
535 % (i, o, o_data, repr(expected))
536
537 def data_2op():
538 data = []
539 for i in range(num_tests):
540 data.append(TestInputAdd(randint(0, 1<<16-1), randint(0, 1<<16-1)))
541 return data
542
543
544 num_tests = 100
545
546 if __name__ == '__main__':
547 print ("test 1")
548 dut = ExampleBufPipe()
549 run_simulation(dut, testbench(dut), vcd_name="test_bufpipe.vcd")
550
551 print ("test 2")
552 dut = ExampleBufPipe2()
553 run_simulation(dut, testbench2(dut), vcd_name="test_bufpipe2.vcd")
554
555 print ("test 3")
556 dut = ExampleBufPipe()
557 test = Test3(dut, test3_resultfn)
558 run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe3.vcd")
559
560 print ("test 3.5")
561 dut = ExamplePipeline()
562 test = Test3(dut, test3_resultfn)
563 run_simulation(dut, [test.send, test.rcv], vcd_name="test_combpipe3.vcd")
564
565 print ("test 4")
566 dut = ExampleBufPipe2()
567 run_simulation(dut, testbench4(dut), vcd_name="test_bufpipe4.vcd")
568
569 print ("test 5")
570 dut = ExampleBufPipeAdd()
571 test = Test5(dut, test5_resultfn)
572 run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe5.vcd")
573
574 print ("test 6")
575 dut = ExampleLTPipeline()
576 test = Test5(dut, test6_resultfn)
577 run_simulation(dut, [test.send, test.rcv], vcd_name="test_ltcomb6.vcd")
578
579 ports = [dut.p.i_valid, dut.n.i_ready,
580 dut.n.o_valid, dut.p.o_ready] + \
581 list(dut.p.i_data) + [dut.n.o_data]
582 vl = rtlil.convert(dut, ports=ports)
583 with open("test_ltcomb_pipe.il", "w") as f:
584 f.write(vl)
585
586 print ("test 7")
587 dut = ExampleAddRecordPipe()
588 data=data_dict()
589 test = Test5(dut, test7_resultfn, data=data)
590 run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord.vcd")
591
592 ports = [dut.p.i_valid, dut.n.i_ready,
593 dut.n.o_valid, dut.p.o_ready,
594 dut.p.i_data.src1, dut.p.i_data.src2,
595 dut.n.o_data.src1, dut.n.o_data.src2]
596 vl = rtlil.convert(dut, ports=ports)
597 with open("test_recordcomb_pipe.il", "w") as f:
598 f.write(vl)
599
600 print ("test 8")
601 dut = ExampleBufPipeAddClass()
602 data=data_2op()
603 test = Test5(dut, test8_resultfn, data=data)
604 run_simulation(dut, [test.send, test.rcv], vcd_name="test_bufpipe8.vcd")
605
606 print ("test 9")
607 dut = ExampleBufPipeChain2()
608 ports = [dut.p.i_valid, dut.n.i_ready,
609 dut.n.o_valid, dut.p.o_ready] + \
610 [dut.p.i_data] + [dut.n.o_data]
611 vl = rtlil.convert(dut, ports=ports)
612 with open("test_bufpipechain2.il", "w") as f:
613 f.write(vl)
614
615 data = data_chain2()
616 test = Test5(dut, test9_resultfn, data=data)
617 run_simulation(dut, [test.send, test.rcv],
618 vcd_name="test_bufpipechain2.vcd")
619
620 print ("test 10")
621 dut = ExampleLTBufferedPipeDerived()
622 test = Test5(dut, test6_resultfn)
623 run_simulation(dut, [test.send, test.rcv], vcd_name="test_ltbufpipe10.vcd")
624 vl = rtlil.convert(dut, ports=ports)
625 with open("test_ltbufpipe10.il", "w") as f:
626 f.write(vl)
627
628 print ("test 11")
629 dut = ExampleAddRecordPlaceHolderPipe()
630 data=data_placeholder()
631 test = Test5(dut, test11_resultfn, data=data)
632 run_simulation(dut, [test.send, test.rcv], vcd_name="test_addrecord.vcd")
633
634