a3f17d1bd4bae86a51082a5d8a0a583aadcfc1ec
[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 o_n_stbn = Signal(reset_less=True)
46 i_p_stb_o_p_busyn = Signal(reset_less=True)
47 m.d.comb += o_n_stbn.eq(~self.o_n_stb)
48 m.d.comb += o_p_busyn.eq(~self.o_p_busy)
49 m.d.comb += i_p_stb_o_p_busyn.eq(self.i_p_stb & o_p_busyn)
50
51 result = Signal(32)
52 m.d.comb += result.eq(self.process(self.i_data))
53 with m.If(o_p_busyn): # not stalled
54 m.d.sync += self.r_data.eq(result)
55
56 #with m.If(self.i_p_rst): # reset
57 # m.d.sync += self.o_n_stb.eq(0)
58 # m.d.sync += self.o_p_busy.eq(0)
59 with m.If(~self.i_n_busy): # previous stage is not busy
60 with m.If(o_p_busyn): # not stalled
61 # nothing in buffer: send input direct to output
62 m.d.sync += self.o_n_stb.eq(self.i_p_stb)
63 m.d.sync += self.o_data.eq(result)
64 with m.Else(): # o_p_busy is true, and something is in our buffer.
65 # Flush the buffer to the output port.
66 m.d.sync += self.o_n_stb.eq(1)
67 m.d.sync += self.o_data.eq(self.r_data)
68 # ignore input, since o_p_busy is also true.
69 # also clear stall condition, declare register to be empty.
70 m.d.sync += self.o_p_busy.eq(0)
71
72 # (i_n_busy) is true here: previous stage is busy
73 with m.Elif(o_n_stbn): # next stage being told "not busy"
74 m.d.sync += self.o_n_stb.eq(self.i_p_stb)
75 m.d.sync += self.o_p_busy.eq(0) # Keep the buffer empty
76 # Apply the logic to the input data, and set the output data
77 m.d.sync += self.o_data.eq(result)
78
79 # (i_n_busy) and (o_n_stb) both true:
80 with m.Elif(i_p_stb_o_p_busyn):
81 # If next stage *is* busy, and not stalled yet, accept input
82 m.d.sync += self.o_p_busy.eq(self.i_p_stb & self.o_n_stb)
83
84 with m.If(o_p_busyn): # not stalled
85 # turns out that from all of the above conditions, just
86 # always put result into buffer if not busy
87 m.d.sync += self.r_data.eq(result)
88
89 return m
90
91 def ports(self):
92 return [self.i_p_stb, self.i_n_busy, self.i_data,
93 self.r_data,
94 self.o_n_stb, self.o_p_busy, self.o_data
95 ]
96
97
98 def testbench(dut):
99 #yield dut.i_p_rst.eq(1)
100 yield dut.i_n_busy.eq(1)
101 yield dut.o_p_busy.eq(1)
102 yield
103 yield
104 #yield dut.i_p_rst.eq(0)
105 yield dut.i_n_busy.eq(0)
106 yield dut.i_data.eq(5)
107 yield dut.i_p_stb.eq(1)
108 yield
109 yield dut.i_data.eq(7)
110 yield
111 yield dut.i_data.eq(2)
112 yield
113 yield dut.i_n_busy.eq(1)
114 yield dut.i_data.eq(9)
115 yield
116 yield dut.i_p_stb.eq(0)
117 yield dut.i_data.eq(12)
118 yield
119 yield dut.i_data.eq(32)
120 yield dut.i_n_busy.eq(0)
121 yield
122 yield
123 yield
124 yield
125
126
127 if __name__ == '__main__':
128 dut = BufPipe()
129 vl = rtlil.convert(dut, ports=dut.ports())
130 with open("test_bufpipe.il", "w") as f:
131 f.write(vl)
132 run_simulation(dut, testbench(dut), vcd_name="test_bufpipe.vcd")
133