gen/genlib/cdc: import gcd from math and not fractions (deprecated)
[litex.git] / litex / gen / genlib / cdc.py
index 90b5f907699c114720d57e9bc9cbb3575f248430..2f9efabc2b5c4958bb853050e853bb3d1de65cc8 100644 (file)
@@ -1,19 +1,14 @@
+"""
+Clock domain crossing module
+"""
+from math import gcd
+
 from litex.gen.fhdl.structure import *
 from litex.gen.fhdl.module import Module
-from litex.gen.fhdl.specials import Special
+from litex.gen.fhdl.specials import Special, Memory
 from litex.gen.fhdl.bitcontainer import value_bits_sign
 from litex.gen.genlib.misc import WaitTimer
-
-
-class NoRetiming(Special):
-    def __init__(self, reg):
-        Special.__init__(self)
-        self.reg = reg
-
-    # do nothing
-    @staticmethod
-    def lower(dr):
-        return Module()
+from litex.gen.genlib.resetsync import AsyncResetSynchronizer
 
 
 class MultiRegImpl(Module):
@@ -33,7 +28,8 @@ class MultiRegImpl(Module):
             sd += reg.eq(src)
             src = reg
         self.comb += self.o.eq(src)
-        self.specials += [NoRetiming(reg) for reg in self.regs]
+        for reg in self.regs:
+            reg.attr.add("no_retiming")
 
 
 class MultiReg(Special):
@@ -113,6 +109,7 @@ class BusSynchronizer(Module):
             ibuffer = Signal(width)
             obuffer = Signal(width)
             sync_i += If(self._pong.o, ibuffer.eq(self.i))
+            ibuffer.attr.add("no_retiming")
             self.specials += MultiReg(ibuffer, obuffer, odomain)
             sync_o += If(self._ping.o, self.o.eq(obuffer))
 
@@ -139,3 +136,117 @@ class GrayCounter(Module):
             self.q_binary.eq(self.q_next_binary),
             self.q.eq(self.q_next)
         ]
+
+
+class GrayDecoder(Module):
+    def __init__(self, width):
+        self.i = Signal(width)
+        self.o = Signal(width)
+
+        # # #
+
+        o_comb = Signal(width)
+        self.comb += o_comb[-1].eq(self.i[-1])
+        for i in reversed(range(width-1)):
+            self.comb += o_comb[i].eq(o_comb[i+1] ^ self.i[i])
+        self.sync += self.o.eq(o_comb)
+
+
+class ElasticBuffer(Module):
+    def __init__(self, width, depth, idomain, odomain):
+        self.din = Signal(width)
+        self.dout = Signal(width)
+
+        # # #
+
+        reset = Signal()
+        cd_write = ClockDomain()
+        cd_read = ClockDomain()
+        self.comb += [
+            cd_write.clk.eq(ClockSignal(idomain)),
+            cd_read.clk.eq(ClockSignal(odomain)),
+            reset.eq(ResetSignal(idomain) | ResetSignal(odomain))
+        ]
+        self.specials += [
+            AsyncResetSynchronizer(cd_write, reset),
+            AsyncResetSynchronizer(cd_read, reset)
+        ]
+        self.clock_domains += cd_write, cd_read
+
+        wrpointer = Signal(max=depth, reset=depth//2)
+        rdpointer = Signal(max=depth)
+
+        storage = Memory(width, depth)
+        self.specials += storage
+
+        wrport = storage.get_port(write_capable=True, clock_domain="write")
+        rdport = storage.get_port(clock_domain="read")
+        self.specials += wrport, rdport
+
+        self.sync.write += wrpointer.eq(wrpointer + 1)
+        self.sync.read += rdpointer.eq(rdpointer + 1)
+
+        self.comb += [
+            wrport.we.eq(1),
+            wrport.adr.eq(wrpointer),
+            wrport.dat_w.eq(self.din),
+
+            rdport.adr.eq(rdpointer),
+            self.dout.eq(rdport.dat_r)
+        ]
+
+
+def lcm(a, b):
+    """Compute the lowest common multiple of a and b"""
+    return int(a * b / gcd(a, b))
+
+
+class Gearbox(Module):
+    def __init__(self, iwidth, idomain, owidth, odomain):
+        self.i = Signal(iwidth)
+        self.o = Signal(owidth)
+
+        # # #
+
+        reset = Signal()
+        cd_write = ClockDomain()
+        cd_read = ClockDomain()
+        self.comb += [
+            cd_write.clk.eq(ClockSignal(idomain)),
+            cd_read.clk.eq(ClockSignal(odomain)),
+            reset.eq(ResetSignal(idomain) | ResetSignal(odomain))
+        ]
+        self.specials += [
+            AsyncResetSynchronizer(cd_write, reset),
+            AsyncResetSynchronizer(cd_read, reset)
+        ]
+        self.clock_domains += cd_write, cd_read
+
+        storage = Signal(lcm(iwidth, owidth))
+        wrchunks = len(storage)//iwidth
+        rdchunks = len(storage)//owidth
+        wrpointer = Signal(max=wrchunks, reset=0 if iwidth > owidth else wrchunks-1)
+        rdpointer = Signal(max=rdchunks, reset=rdchunks-1 if iwidth > owidth else 0)
+
+        self.sync.write += \
+            If(wrpointer == wrchunks-1,
+                wrpointer.eq(0)
+            ).Else(
+                wrpointer.eq(wrpointer + 1)
+            )
+        cases = {}
+        for i in range(wrchunks):
+            cases[i] = [storage[iwidth*i:iwidth*(i+1)].eq(self.i)]
+        self.sync.write += Case(wrpointer, cases)
+
+
+        self.sync.read += \
+            If(rdpointer == rdchunks-1,
+                rdpointer.eq(0)
+            ).Else(
+                rdpointer.eq(rdpointer + 1)
+            )
+        cases = {}
+        for i in range(rdchunks):
+            cases[i] = [self.o.eq(storage[owidth*i:owidth*(i+1)])]
+        self.sync.read += Case(rdpointer, cases)
\ No newline at end of file