Source code for hwtLib.clocking.cdc
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Tuple, Optional
from hwt.code import If
from hwt.constraints import set_max_delay, set_async_reg
from hwt.hdl.types.bits import Bits
from hwt.interfaces.std import Rst, Signal, Clk
from hwt.synthesizer.interfaceLevel.unitImplHelpers import getSignalName
from hwt.synthesizer.param import Param
from hwt.synthesizer.rtlLevel.mainBases import RtlSignalBase
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
from hwt.synthesizer.unit import Unit
[docs]class SignalCdcBuilder():
"""
Object which can build CDCs for simple Signal interfaces.
It automatically adding constrains and check correnctnes of CDC path.
Main purpose of this class is to allow building
of CDCs without requirement of component instantiation.
"""
[docs] def __init__(self, sig,
in_clk_rst: Tuple[RtlSignal, RtlSignal],
out_clk_rst: Tuple[RtlSignal, RtlSignal],
reg_init_val, name_prefix: Optional[str]=None):
"""
:param sig: data in signal
:param in_clk_rst: tuple source clk signal, rst signal
:param out_clk_rst: tuple destination clk signal, rst signal
:note: rst/rst_n is automatically resolved from reset type
:param reg_init_val: register reset value
"""
self.path = [sig, ]
if name_prefix is None:
name_prefix = getSignalName(sig) + "_"
self.name_prefix = name_prefix
self.IN_CLK_RST = in_clk_rst
self.OUT_CLK_RST = out_clk_rst
self.REG_INIT_VAL = reg_init_val
self.META_PERIOD_NS =\
1e9 / max(in_clk_rst[0].FREQ, in_clk_rst[0].FREQ) * 0.5
self.in_reg_cnt = 0
self.out_reg_cnt = 0
[docs] def _parent(self):
i = self.path[0]
if isinstance(i, RtlSignalBase):
return i.ctx.parent
else:
# interface
i = i._parent
while not isinstance(i, Unit):
i = i._parent
return i
[docs] def _reg(self, name, clk_rst):
clk, rst = clk_rst
din = self.path[-1]
return self._parent()._reg(
name,
din._dtype,
clk=clk,
rst=rst,
def_val=self.REG_INIT_VAL)
[docs] def add_in_reg(self):
assert self.out_reg_cnt == 0, self.out_reg_cnt
inReg = self._reg(f"{self.name_prefix:s}in_reg{self.in_reg_cnt:d}",
self.IN_CLK_RST)
inReg(self.path[-1])
self.path.append(inReg)
self.in_reg_cnt += 1
[docs] def add_out_reg(self, en: Optional[RtlSignal]=None):
path = self.path
outReg = self._reg(f"{self.name_prefix:s}out_reg{self.out_reg_cnt:d}",
self.OUT_CLK_RST)
end = path[-1]
if en is not None:
If(en,
outReg(end)
)
else:
outReg(end)
set_max_delay(end, outReg, self.META_PERIOD_NS)
set_async_reg(outReg)
path.append(outReg)
self.out_reg_cnt += 1
[docs]class Cdc(Unit):
"""
CDC (Clock Domain Crossing) for Signal interface
(Synchronizes the signal for different clock domain.)
:attention: regular multibits signals should not be sychronized using
this sychronizer instead handshake or req-ack sychronization
should be used for control signals and main data should be
passed over couple of registers
:ivar ~.DATA_WIDTH: width of data-signal
:ivar ~.INIT_VAL: initialization value for registers
:ivar ~.IN_FREQ: frequency of clock signal for input data [Hz]
:ivar ~.OUT_FREQ: frequency of clock signal for output data [Hz]
:ivar ~.OUT_REG_CNT: number of registers for synchronization in dataOut clock domain
.. hwt-autodoc::
"""
def _config(self):
self.DATA_WIDTH = Param(1)
self.INIT_VAL = Param(0)
self.IN_FREQ = Param(100e6)
self.OUT_FREQ = Param(100e6)
self.OUT_REG_CNT = Param(2)
def _declr(self):
assert self.OUT_REG_CNT >= 2, self.OUT_REG_CNT
self.dataIn_clk = Clk()
self.dataIn_clk.FREQ = self.IN_FREQ
with self._associated(clk=self.dataIn_clk):
self.dataIn_rst = Rst()
with self._associated(rst=self.dataIn_rst):
self.dataIn = Signal(dtype=Bits(self.DATA_WIDTH))
self.dataOut_clk = Clk()
self.dataOut_clk.FREQ = self.OUT_FREQ
with self._associated(clk=self.dataOut_clk):
self.dataOut_rst = Rst()
with self._associated(rst=self.dataOut_rst):
self.dataOut = Signal(dtype=Bits(self.DATA_WIDTH))._m()
def _impl(self):
in_clk_rst = self.dataIn_clk, self.dataIn_rst
out_clk_rst = self.dataOut_clk, self.dataOut_rst
b = SignalCdcBuilder(
self.dataIn, in_clk_rst, out_clk_rst,
self.INIT_VAL, name_prefix="")
b.add_in_reg()
for _ in range(self.OUT_REG_CNT):
b.add_out_reg()
self.dataOut(b.path[-1])
return b.path
[docs]class CdcPulseGen(Cdc):
"""
.. code-block:: python
if inData_clk.FREQ > outData_clk.FREQ:
outData_clk.FREQ >= 1.5*inData_clk.FREQ
else:
inData_clk.FREQ >= 1.5*outData_clk.FREQ
.. hwt-autodoc::
"""
def _config(self):
Cdc._config(self)
self.OUT_REG_CNT = 3
def _declr(self):
assert self.DATA_WIDTH == 1, self.DATA_WIDTH
Cdc._declr(self)
with self._associated(clk=self.dataOut_clk, rst=self.dataOut_rst):
self.dataOut_en = Signal()._m()
def _impl(self):
(_, _, _, out_reg1, out_reg2) = Cdc._impl(self)
self.dataOut_en(out_reg1 ^ out_reg2)
if __name__ == "__main__":
from hwt.synthesizer.utils import to_rtl_str
print(to_rtl_str(Cdc()))