diff --git a/gearbox.py b/gearbox.py
index 1ffb0f4c685dbd44faf8c0a080d44725727f36a3..3283bc9ea461aaf3e89f3b43d5c66d79f4f3bc0f 100755
--- a/gearbox.py
+++ b/gearbox.py
@@ -1,6 +1,7 @@
 from enum import Enum
 from nmigen import *
 from nmigen.hdl.rec import *
+from nmigen.asserts import *
 from nmigen.cli import main
 
 
@@ -82,7 +83,7 @@ class GearboxFlowControl(Elaboratable):
         self.len_storage = len_storage
 
         self.bus = GearboxFCBus(len_storage=len_storage)
-        print(len(self.bus.read_ptr))
+        #print(len(self.bus.read_ptr))
 
 
     def elaborate(self, platform):
@@ -235,7 +236,7 @@ class ArbitraryGearbox(Elaboratable):
 
             with m.If(self.bus.fault == 0):
                 # read index update:
-                with m.If(0 == 1):
+                with m.If(self.bus.ready_in == 1):
                     with m.If(read_ptr + self.out_width >= len_storage):
                         m.d.sync += read_ptr.eq(read_ptr + self.out_width - len_storage)
                     with m.Else():
@@ -280,13 +281,80 @@ class ArbitraryGearbox(Elaboratable):
                                     ))
 
 
+            #m.d.sync += Assert(read_ptr == 0)
 
+            #m.d.comb += Assume((self.bus.data_in == 3) |( self.bus.data_in==5))
 
 
             return m
 
 
 
+class IdempotentGearbox(Elaboratable):
+        def __init__(self, *, in_width, out_width):
+            self.in_width = in_width
+            self.out_width = out_width
+            assert(in_width == out_width)
+
+            self.bus = GearboxBus(in_width=in_width, out_width=out_width)
+
+        def elaborate(self, platform):
+            m = Module()
+            skid_buffer = Signal(self.in_width)
+            skid_buffer_frozen = Signal(1)
+
+            # we signal ready to upstream if and only if the skid buffer is empty
+            m.d.comb += self.bus.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       (bus.valid_in   == 1)
+            # 2. a buffer that is empty (skid_buffer_frozen  == 0)
+            # 3. stalled downstream     (bus.ready_in == 0) & (bus.valid_out == 1)
+
+            with m.If(self.bus.valid_in & (~skid_buffer_frozen)  & (~self.bus.ready_in) & (self.bus.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.bus.ready_in == 1):
+                m.d.sync += skid_buffer_frozen.eq(0)
+
+
+            # Stalled == (bus.ready_in == 0 & bus.valid_out == 1)
+            # so not stalled = !(bus.ready_in == 0 & bus.valid_out == 1)
+            #    = bus.ready_in == 1 | bus.valid_out == 0
+            # by de Morgan
+
+            with m.If((self.bus.ready_in) | (~self.bus.valid_out)):
+                m.d.sync += self.bus.valid_out.eq(self.bus.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.bus.data_in)
+
+            # not stalled condition
+            with m.If((self.bus.ready_in) | (~self.bus.valid_out)):
+                with m.If(skid_buffer_frozen == 1):
+                    m.d.sync += self.bus.data_out.eq(skid_buffer)
+                with m.Elif(self.bus.valid_in == 1):
+                    m.d.sync += self.bus.data_out.eq(self.bus.data_in)
+
+
+            eggdrop = Signal(3)
+
+            m.d.comb += eggdrop.eq(Past(self.bus.data_in))
+
+            with m.If(self.bus.ready_in == 1):
+                m.d.sync += Assert(self.bus.data_out == eggdrop)
+
+
+            return m
 
 class DummyPlug(Elaboratable):
 
@@ -297,12 +365,15 @@ class DummyPlug(Elaboratable):
     def elaborate(self, platform):
         m = Module()
 
-        m.submodules.gearbox = gearbox = ArbitraryGearbox(in_width=(9-3), out_width=3)
-        counter = Signal(8)
-        m.d.sync += counter.eq(counter+1)
+        m.submodules.gearbox = gearbox = IdempotentGearbox(in_width=(3), out_width=3)
+        #counter = Signal(8)
+        #m.d.sync += counter.eq(counter+1)
 
-        with m.If(counter == 3):
-            m.d.comb += gearbox.bus.data_in.eq(1)
+#        with m.If(counter == 3):
+#            m.d.comb += gearbox.bus.valid_in.eq(1)
+        m.d.comb += gearbox.bus.data_in.eq(AnySeq(3))
+        m.d.comb += gearbox.bus.ready_in.eq(AnySeq(1))
+        m.d.comb += gearbox.bus.valid_in.eq(AnySeq(1))
 
 
         return m