clean up code
[ieee754fpu.git] / src / add / example_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 from nmigen import Signal, Cat, Const, Mux, Module
5 from nmigen.compat.sim import run_simulation
6 from nmigen.cli import verilog, rtlil
7
8 class BufPipe:
9 """ buffered pipeline stage
10
11 stage-1 i_p_stb >>in stage o_n_stb out>> stage+1
12 stage-1 o_p_busy <<out stage i_n_busy <<in stage+1
13 stage-1 i_data >>in stage o_data out>> stage+1
14 | |
15 +-------> process
16 | |
17 +-- r_data ---+
18 """
19 def __init__(self):
20 # input
21 #self.i_p_rst = Signal() # >>in - comes in from PREVIOUS stage
22 self.i_p_stb = Signal() # >>in - comes in from PREVIOUS stage
23 self.i_n_busy = Signal() # in<< - comes in from the NEXT stage
24 self.i_data = Signal(32) # >>in - comes in from the PREVIOUS stage
25 #self.i_rst = Signal()
26
27 # buffered
28 self.r_data = Signal(32)
29
30 # output
31 self.o_n_stb = Signal() # out>> - goes out to the NEXT stage
32 self.o_p_busy = Signal() # <<out - goes out to the PREVIOUS stage
33 self.o_data = Signal(32) # out>> - goes out to the NEXT stage
34
35 def pre_process(self, d_in):
36 return d_in | 0xf0000
37
38 def process(self, d_in):
39 return d_in + 1
40
41 def elaborate(self, platform):
42 m = Module()
43
44 o_p_busyn = Signal(reset_less=True)
45 i_p_stb_o_p_busyn = Signal(reset_less=True)
46 m.d.comb += o_p_busyn.eq(~self.o_p_busy)
47 m.d.comb += i_p_stb_o_p_busyn.eq(self.i_p_stb & o_p_busyn)
48
49 result = Signal(32)
50 m.d.comb += result.eq(self.process(self.i_data))
51 with m.If(o_p_busyn): # not stalled
52 m.d.sync += self.r_data.eq(result)
53
54 #with m.If(self.i_p_rst): # reset
55 # m.d.sync += self.o_n_stb.eq(0)
56 # m.d.sync += self.o_p_busy.eq(0)
57 with m.If(~self.i_n_busy): # previous stage is not busy
58 with m.If(o_p_busyn): # not stalled
59 # nothing in buffer: send input direct to output
60 m.d.sync += self.o_n_stb.eq(self.i_p_stb)
61 m.d.sync += self.o_data.eq(result)
62 with m.Else(): # o_p_busy is true, and something is in our buffer.
63 # Flush the buffer to the output port.
64 m.d.sync += self.o_n_stb.eq(1)
65 m.d.sync += self.o_data.eq(self.r_data)
66 # ignore input, since o_p_busy is also true.
67 # also clear stall condition, declare register to be empty.
68 m.d.sync += self.o_p_busy.eq(0)
69
70 # (i_n_busy) is true here: previous stage is busy
71 with m.Elif(~self.o_n_stb): # next stage being told "not busy"
72 m.d.sync += self.o_n_stb.eq(self.i_p_stb)
73 m.d.sync += self.o_p_busy.eq(0) # Keep the buffer empty
74 # Apply the logic to the input data, and set the output data
75 m.d.sync += self.o_data.eq(result)
76
77 # (i_n_busy) and (o_n_stb) both true:
78 with m.Elif(i_p_stb_o_p_busyn):
79 # If next stage *is* busy, and not stalled yet, accept input
80 m.d.sync += self.o_p_busy.eq(self.i_p_stb & self.o_n_stb)
81
82 with m.If(o_p_busyn): # not stalled
83 # turns out that from all of the above conditions, just
84 # always put result into buffer if not busy
85 m.d.sync += self.r_data.eq(result)
86
87 return m
88
89 def ports(self):
90 return [self.i_p_stb, self.i_n_busy, self.i_data,
91 self.r_data,
92 self.o_n_stb, self.o_p_busy, self.o_data
93 ]
94
95
96 def testbench(dut):
97 #yield dut.i_p_rst.eq(1)
98 yield dut.i_n_busy.eq(1)
99 yield dut.o_p_busy.eq(1)
100 yield
101 yield
102 #yield dut.i_p_rst.eq(0)
103 yield dut.i_n_busy.eq(0)
104 yield dut.i_data.eq(5)
105 yield dut.i_p_stb.eq(1)
106 yield
107 yield dut.i_data.eq(7)
108 yield
109 yield dut.i_data.eq(2)
110 yield
111 yield dut.i_n_busy.eq(1)
112 yield dut.i_data.eq(9)
113 yield
114 yield dut.i_p_stb.eq(0)
115 yield dut.i_data.eq(12)
116 yield
117 yield dut.i_data.eq(32)
118 yield dut.i_n_busy.eq(0)
119 yield
120 yield
121 yield
122 yield
123
124
125 if __name__ == '__main__':
126 dut = BufPipe()
127 vl = rtlil.convert(dut, ports=dut.ports())
128 with open("test_bufpipe.il", "w") as f:
129 f.write(vl)
130 run_simulation(dut, testbench(dut), vcd_name="test_bufpipe.vcd")
131