lib.cdc: extract AsyncFFSynchronizer.
[nmigen.git] / nmigen / hdl / cd.py
1 from .. import tracer
2 from .ast import Signal
3
4
5 __all__ = ["ClockDomain", "DomainError"]
6
7
8 class DomainError(Exception):
9 pass
10
11
12 class ClockDomain:
13 """Synchronous domain.
14
15 Parameters
16 ----------
17 name : str or None
18 Domain name. If ``None`` (the default) the name is inferred from the variable name this
19 ``ClockDomain`` is assigned to (stripping any `"cd_"` prefix).
20 reset_less : bool
21 If ``True``, the domain does not use a reset signal. Registers within this domain are
22 still all initialized to their reset state once, e.g. through Verilog `"initial"`
23 statements.
24 clock_edge : str
25 The edge of the clock signal on which signals are sampled. Must be one of "pos" or "neg".
26 async_reset : bool
27 If ``True``, the domain uses an asynchronous reset, and registers within this domain
28 are initialized to their reset state when reset level changes. Otherwise, registers
29 are initialized to reset state at the next clock cycle when reset is asserted.
30 local : bool
31 If ``True``, the domain will propagate only downwards in the design hierarchy. Otherwise,
32 the domain will propagate everywhere.
33
34 Attributes
35 ----------
36 clk : Signal, inout
37 The clock for this domain. Can be driven or used to drive other signals (preferably
38 in combinatorial context).
39 rst : Signal or None, inout
40 Reset signal for this domain. Can be driven or used to drive.
41 """
42
43 @staticmethod
44 def _name_for(domain_name, signal_name):
45 if domain_name == "sync":
46 return signal_name
47 else:
48 return "{}_{}".format(domain_name, signal_name)
49
50 def __init__(self, name=None, *, clk_edge="pos", reset_less=False, async_reset=False,
51 local=False):
52 if name is None:
53 try:
54 name = tracer.get_var_name()
55 except tracer.NameNotFound:
56 raise ValueError("Clock domain name must be specified explicitly")
57 if name.startswith("cd_"):
58 name = name[3:]
59 if name == "comb":
60 raise ValueError("Domain '{}' may not be clocked".format(name))
61
62 if clk_edge not in ("pos", "neg"):
63 raise ValueError("Domain clock edge must be one of 'pos' or 'neg', not {!r}"
64 .format(clk_edge))
65
66 self.name = name
67
68 self.clk = Signal(name=self._name_for(name, "clk"), src_loc_at=1)
69 self.clk_edge = clk_edge
70
71 if reset_less:
72 self.rst = None
73 else:
74 self.rst = Signal(name=self._name_for(name, "rst"), src_loc_at=1)
75
76 self.async_reset = async_reset
77
78 self.local = local
79
80 def rename(self, new_name):
81 self.name = new_name
82 self.clk.name = self._name_for(new_name, "clk")
83 if self.rst is not None:
84 self.rst.name = self._name_for(new_name, "rst")