+"""
+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.fhdl.decorators import ClockDomainsRenamer
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):
self.odomain = odomain
w, signed = value_bits_sign(self.i)
- self.regs = [Signal((w, signed)) for i in range(n)]
+ self.regs = [Signal((w, signed), reset_less=True)
+ for i in range(n)]
###
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):
###
- toggle_i = Signal()
- toggle_o = Signal()
- toggle_o_r = Signal()
+ toggle_i = Signal(reset_less=True)
+ toggle_o = Signal() # registered reset_less by MultiReg
+ toggle_o_r = Signal(reset_less=True)
sync_i = getattr(self.sync, idomain)
sync_o = getattr(self.sync, odomain)
``MultiReg``)."""
def __init__(self, width, idomain, odomain, timeout=128):
self.i = Signal(width)
- self.o = Signal(width)
+ self.o = Signal(width, reset_less=True)
if width == 1:
self.specials += MultiReg(self.i, self.o, odomain)
sync_i += starter.eq(0)
self.submodules._ping = PulseSynchronizer(idomain, odomain)
self.submodules._pong = PulseSynchronizer(odomain, idomain)
- self.submodules._timeout = WaitTimer(timeout)
+ self.submodules._timeout = ClockDomainsRenamer(idomain)(
+ WaitTimer(timeout))
self.comb += [
self._timeout.wait.eq(~self._ping.i),
self._ping.i.eq(starter | self._pong.o | self._timeout.done),
self._pong.i.eq(self._ping.i)
]
- ibuffer = Signal(width)
- obuffer = Signal(width)
+ ibuffer = Signal(width, reset_less=True)
+ obuffer = Signal(width) # registered reset_less by MultiReg
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))
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, reset_less=True)
+
+ # # #
+
+ 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 (a*b)//gcd(a, b)
+
+
+class Gearbox(Module):
+ def __init__(self, iwidth, idomain, owidth, odomain):
+ self.i = Signal(iwidth)
+ self.o = Signal(owidth, reset_less=True)
+
+ # # #
+
+ rst = Signal()
+ cd_write = ClockDomain()
+ cd_read = ClockDomain()
+ self.comb += [
+ rst.eq(ResetSignal(idomain) | ResetSignal(odomain)),
+ cd_write.clk.eq(ClockSignal(idomain)),
+ cd_read.clk.eq(ClockSignal(odomain)),
+ cd_write.rst.eq(rst),
+ cd_read.rst.eq(rst)
+ ]
+ self.clock_domains += cd_write, cd_read
+
+ storage = Signal(2*lcm(iwidth, owidth), reset_less=True)
+ wrchunks = len(storage)//iwidth
+ rdchunks = len(storage)//owidth
+ wrpointer = Signal(max=wrchunks, reset=0 if iwidth > owidth else wrchunks//2)
+ rdpointer = Signal(max=rdchunks, reset=rdchunks//2 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