add in some assertions to check pipe output
[ieee754fpu.git] / src / add / test_buf_pipe.py
1 """ nmigen implementation of buffered pipeline stage, based on zipcpu:
2 https://zipcpu.com/blog/2017/08/14/strategies-for-pipelining.html
3
4 this module requires quite a bit of thought to understand how it works
5 (and why it is needed in the first place). reading the above is
6 *strongly* recommended.
7
8 unlike john dawson's IEEE754 FPU STB/ACK signalling, which requires
9 the STB / ACK signals to raise and lower (on separate clocks) before
10 data may proceeed (thus only allowing one piece of data to proceed
11 on *ALTERNATE* cycles), the signalling here is a true pipeline
12 where data will flow on *every* clock when the conditions are right.
13
14 input acceptance conditions are when:
15 * incoming previous-stage strobe (i_p_stb) is HIGH
16 * outgoing previous-stage busy (o_p_busy) is LOW
17
18 output transmission conditions are when:
19 * outgoing next-stage strobe (o_n_stb) is HIGH
20 * outgoing next-stage busy (i_n_busy) is LOW
21
22 the tricky bit is when the input has valid data and the output is not
23 ready to accept it. if it wasn't for the clock synchronisation, it
24 would be possible to tell the input "hey don't send that data, we're
25 not ready". unfortunately, it's not possible to "change the past":
26 the previous stage *has no choice* but to pass on its data.
27
28 therefore, the incoming data *must* be accepted - and stored.
29 on the same clock, it's possible to tell the input that it must
30 not send any more data. this is the "stall" condition.
31
32 we now effectively have *two* possible pieces of data to "choose" from:
33 the buffered data, and the incoming data. the decision as to which
34 to process and output is based on whether we are in "stall" or not.
35 i.e. when the next stage is no longer busy, the output comes from
36 the buffer if a stall had previously occurred, otherwise it comes
37 direct from processing the input.
38
39 it's quite a complex state machine!
40 """
41
42 from nmigen.compat.sim import run_simulation
43 from example_buf_pipe import BufPipe
44
45 def check_o_n_stb(dut, val):
46 o_n_stb = yield dut.o_n_stb
47 assert o_n_stb == val
48
49
50 def testbench(dut):
51 #yield dut.i_p_rst.eq(1)
52 yield dut.i_n_busy.eq(1)
53 yield dut.o_p_busy.eq(1)
54 yield
55 yield
56 #yield dut.i_p_rst.eq(0)
57 yield dut.i_n_busy.eq(0)
58 yield dut.i_data.eq(5)
59 yield dut.i_p_stb.eq(1)
60 yield
61
62 yield dut.i_data.eq(7)
63 yield from check_o_n_stb(dut, 0) # effects of i_p_stb delayed
64 yield
65 yield from check_o_n_stb(dut, 1) # ok *now* i_p_stb effect is felt
66
67 yield dut.i_data.eq(2)
68 yield
69 yield dut.i_n_busy.eq(1)
70 yield dut.i_data.eq(9)
71 yield
72 yield dut.i_p_stb.eq(0)
73 yield dut.i_data.eq(12)
74 yield
75 yield dut.i_data.eq(32)
76 yield dut.i_n_busy.eq(0)
77 yield
78 yield from check_o_n_stb(dut, 1) # buffer still needs to output
79 yield
80 yield from check_o_n_stb(dut, 1) # buffer still needs to output
81 yield
82 yield from check_o_n_stb(dut, 0) # buffer outputted, *now* we're done.
83 yield
84
85
86 if __name__ == '__main__':
87 dut = BufPipe()
88 run_simulation(dut, testbench(dut), vcd_name="test_bufpipe.vcd")
89