From f901ac1438edde930b17868f2755d2484f70d689 Mon Sep 17 00:00:00 2001
From: Kia <kia@special-circumstanc.es>
Date: Sat, 13 Mar 2021 18:46:34 -0700
Subject: [PATCH] generic pipeline stage register, with registered and
 non-registered ready

---
 PipeStage.py | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+)
 create mode 100644 PipeStage.py

diff --git a/PipeStage.py b/PipeStage.py
new file mode 100644
index 0000000..96f212e
--- /dev/null
+++ b/PipeStage.py
@@ -0,0 +1,163 @@
+from nmigen import *
+from nmigen.cli import main
+
+
+
+
+
+
+class PipeStage(Elaboratable):
+    def __init__(self, width, *, registered_ready):
+        self.width = width
+        self.registered_ready = registered_ready
+
+        self.upstream_valid_in =    Signal(1)
+        self.upstream_ready_out =   Signal(1)
+        self.upstream_data_in =     Signal(self.width)
+
+        self.downstream_valid_out = Signal(1)
+        self.downstream_ready_in =  Signal(1)
+        self.downstream_data_out =  Signal(self.width)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        if (self.registered_ready == False):
+            m.d.comb += self.upstream_ready_out.eq(self.downstream_ready | (~self.downstream_valid))
+
+            with m.If(self.upstream_ready_out == 1):
+                m.d.sync +=  self.downstream_data_out.eq(self.upstream_data_in)
+                m.d.sync += self.downstream_valid_out.eq(self.upstream_valid_in)
+
+        else:
+            pipe_reg = Signal(self.width)
+            skid_reg = Signal(self.width)
+
+            pipe_reg_valid = Signal(1)
+            skid_reg_valid = Signal(1)
+
+            m.d.comb += self.downstream_valid_out.eq( skid_reg_valid | pipe_reg_valid)
+            m.d.comb +=   self.upstream_ready_out.eq(~skid_reg_valid)
+
+            with m.If(skid_reg_valid == 1):
+                m.d.comb += self.downstream_data_out.eq(skid_reg)
+            with m.Else():
+                m.d.comb += self.downstream_data_out.eq(pipe_reg)
+
+            with m.If(self.downstream_ready_in == 1):
+                m.d.sync += skid_reg_valid.eq(0)
+
+            with m.If(self.upstream_ready_out == 1):
+                m.d.sync +=       pipe_reg.eq(self.upstream_data_in)
+                m.d.sync += pipe_reg_valid.eq(self.upstream_valid_in)
+
+                with m.If(self.downstream_ready_in == 0):
+                    m.d.sync +=       skid_reg.eq(pipe_reg)
+                    m.d.sync += skid_reg_valid.eq(pipe_reg_valid)
+
+        return m
+
+
+
+
+class RegisteredPipeStage(Elaboratable): # registered
+    def __init__(self, width):
+        self.width = width
+
+        self.upstream_valid_in =    Signal(1)
+        self.upstream_ready_out =   Signal(1)
+        self.upstream_data_in =     Signal(self.width)
+
+        self.downstream_valid_out = Signal(1)
+        self.downstream_ready_in =  Signal(1)
+        self.downstream_data_out =  Signal(self.width)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        skid_buffer = Signal(self.width)
+        skid_buffer_frozen = Signal(1)
+
+        # we signal ready to upstream if and only if the skid buffer is empty
+        m.d.comb += self.upstream_ready_out.eq(~skid_buffer_frozen)
+
+
+        # should we capture a value in the skid buffer?
+        # we should if upstream has data for us *and* downstream cannot accept
+
+        # If we want to fill our skid buffer, we need:
+        # 1. valid input data       (upstream_valid_in   == 1)
+        # 2. a buffer that is empty (skid_buffer_frozen  == 0)
+        # 3. stalled downstream     (downstream_ready_in == 0) & (downstream_valid_out == 1)
+
+        with m.If(self.upstream_valid_in & (~skid_buffer_frozen)  & (~self.downstream_ready_in) & (self.downstream_valid_out)):
+            m.d.sync += skid_buffer_frozen.eq(1)
+
+        # if downstream is accepting data again, we will flush our skid buffer
+        with m.If(self.downstream_ready_in == 1):
+            m.d.sync += skid_buffer_frozen.eq(0)
+
+
+        # Stalled == (downstream_ready_in == 0 & downstream_valid_out == 1)
+        # so not stalled = !(downstream_ready_in == 0 & downstream_valid_out == 1)
+        #    = downstream_ready_in == 1 | downstream_valid_out == 0
+        # by de Morgan
+
+        with m.If((self.downstream_ready_in) | (~self.downstream_valid_out)):
+            m.d.sync += self.downstream_valid_out.eq(self.upstream_valid_in | skid_buffer_frozen)
+
+        # data path
+
+        # always clock data into the skid buffer as long as the buffer isn't filled
+
+        with m.If(skid_buffer_frozen == 0):
+            m.d.sync += skid_buffer.eq(self.upstream_data_in)
+
+        # not stalled condition
+        with m.If((self.downstream_ready_in) | (~self.downstream_valid_out)):
+            with m.If(skid_buffer_frozen == 1):
+                m.d.sync += self.downstream_data_out.eq(skid_buffer)
+            with m.Elif(self.upstream_valid_in == 1):
+                m.d.sync += self.downstream_data_out.eq(self.upstream_data_in)
+
+        return m
+
+
+
+
+class DummyPlug(Elaboratable):
+    def elaborate(self, platform):
+        m = Module()
+        egg = RegisteredSkidBuffer(8)
+        m.submodules += egg
+
+        cntr = Signal(8)
+        intctr = Signal(8)
+        data_in = Signal(8)
+        m.d.sync += intctr.eq(intctr +1)
+        m.d.comb += egg.upstream_data_in.eq(data_in)
+
+        upstream_valid = Signal(1)
+        m.d.comb += egg.upstream_valid_in.eq(upstream_valid)
+        m.d.comb += upstream_valid.eq(1)
+
+        downstream_ready = Signal(1)
+        m.d.comb += egg.downstream_ready_in.eq(downstream_ready)
+        m.d.comb += downstream_ready.eq(1)
+        m.d.comb += data_in.eq(cntr)
+        with m.If((intctr == 4) | (intctr == 5) | (intctr == 6)):
+            m.d.comb += downstream_ready.eq(0)
+#        with m.If((intctr == 4) | (intctr == 5)):
+#            m.d.comb += upstream_valid.eq(0)
+#            m.d.comb += data_in.eq(0)
+
+        with m.If(egg.upstream_ready_out == 1):
+            m.d.sync += cntr.eq(cntr+1)
+
+
+        return m
+
+
+if __name__ == '__main__':
+    baka =DummyPlug()
+    main(baka)
\ No newline at end of file
-- 
GitLab