add example buffered pipe
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 12 Mar 2019 13:13:33 +0000 (13:13 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 12 Mar 2019 13:13:33 +0000 (13:13 +0000)
src/add/example_buf_pipe.py [new file with mode: 0644]

diff --git a/src/add/example_buf_pipe.py b/src/add/example_buf_pipe.py
new file mode 100644 (file)
index 0000000..20e91e4
--- /dev/null
@@ -0,0 +1,98 @@
+from nmigen import Signal, Cat, Const, Mux, Module
+from nmigen.compat.sim import run_simulation
+from nmigen.cli import verilog, rtlil
+
+class BufPipe:
+    def __init__(self):
+        # input
+        self.i_p_stb = Signal()    # >>in - comes in from PREVIOUS stage
+        self.i_n_busy = Signal()   # in<< - comes in from the NEXT stage
+        self.i_data = Signal(32) # >>in - comes in from the PREVIOUS stage
+        #self.i_rst = Signal()
+
+        # buffered
+        self.r_data = Signal(32)
+
+        # output
+        self.o_n_stb = Signal()    # out>> - goes out to the NEXT stage
+        self.o_p_busy = Signal()   # <<out - goes out to the PREVIOUS stage
+        self.o_data = Signal(32) # out>> - goes out to the NEXT stage
+
+    def pre_process(self, d_in):
+        return d_in
+
+    def process(self, d_in):
+        return d_in + 1
+
+    def elaborate(self, platform):
+        m = Module()
+
+        i_p_stb_o_p_busyn = Signal(reset_less=True)
+        m.d.comb += i_p_stb_o_p_busyn.eq(self.i_p_stb & (~self.o_p_busy))
+
+        with m.If(~self.i_n_busy): # previous stage is not busy
+            with m.If(~self.o_p_busy): # not stalled
+                # nothing in buffer: send input direct to output
+                m.d.sync += self.o_n_stb.eq(self.i_p_stb)
+                m.d.sync += self.o_data.eq(self.process(self.i_data))
+            with m.Else(): # o_p_busy is true, and something is in our buffer.
+                # Flush the buffer to the output port.
+                m.d.sync += self.o_n_stb.eq(1)
+                m.d.sync += self.o_data.eq(self.r_data)
+                # ignore input, since o_p_busy is also true.
+            # also clear stall condition, declare register to be empty.
+            m.d.sync += self.o_p_busy.eq(0)
+
+        # (i_n_busy) is true here: previous stage is busy
+        with m.Elif(~self.o_n_stb): # next stage being told "not busy"
+            m.d.sync += self.o_n_stb.eq(self.i_p_stb)
+            m.d.sync += self.o_p_busy.eq(0) # Keep the buffer empty
+            # Apply the logic to the input data, and set the output data
+            m.d.sync += self.o_data.eq(self.process(self.i_data))
+
+        # (i_n_busy) and (o_n_stb) both true:
+        with m.Elif(i_p_stb_o_p_busyn):
+            # If next stage *is* busy, and not stalled yet, accept requested
+            # input and store in temporary
+            m.d.sync += self.o_p_busy.eq(self.i_p_stb & self.o_n_stb)
+            with m.If(~self.o_n_stb):
+                m.d.sync += self.r_data.eq(self.i_data)
+
+        with m.If(~self.o_p_busy): # not stalled
+            m.d.sync += self.r_data.eq(self.pre_process(self.i_data))
+
+        return m
+
+    def ports(self):
+        return [self.i_p_stb, self.i_n_busy, self.i_data,
+                self.r_data,
+                self.o_n_stb, self.o_p_busy, self.o_data
+               ]
+
+
+def testbench(dut):
+    yield dut.i_data.eq(5)
+    yield dut.i_p_stb.eq(1)
+    yield
+    yield dut.i_data.eq(7)
+    yield
+    yield dut.i_data.eq(2)
+    yield
+    yield dut.i_n_busy.eq(1)
+    yield dut.i_data.eq(9)
+    yield
+    yield dut.i_data.eq(12)
+    yield
+    yield dut.i_n_busy.eq(0)
+    yield
+    yield
+    yield
+
+
+if __name__ == '__main__':
+    dut = BufPipe()
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_bufpipe.il", "w") as f:
+        f.write(vl)
+    run_simulation(dut, testbench(dut), vcd_name="test_bufpipe.vcd")
+