hdl._ir, lib, vendor: add RequirePosedge
, use it whenever required.
This commit is contained in:
parent
d3c312cf96
commit
028d5d8073
|
@ -10,8 +10,9 @@ from . import _ast, _cd, _ir, _nir
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AlreadyElaborated", "UnusedElaboratable", "Elaboratable", "DuplicateElaboratable",
|
"AlreadyElaborated", "UnusedElaboratable", "Elaboratable", "DuplicateElaboratable",
|
||||||
"DriverConflict", "Fragment", "Instance", "IOBufferInstance", "PortDirection", "Design",
|
"DomainRequirementFailed", "DriverConflict",
|
||||||
"build_netlist",
|
"Fragment", "Instance", "IOBufferInstance", "RequirePosedge", "PortDirection",
|
||||||
|
"Design", "build_netlist",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,6 +41,11 @@ class DuplicateElaboratable(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DomainRequirementFailed(Exception):
|
||||||
|
"""Raised when a module has unsatisfied requirements about a clock domain, such as getting
|
||||||
|
a negedge domain when only posedge domains are supported."""
|
||||||
|
|
||||||
|
|
||||||
class Fragment:
|
class Fragment:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(obj, platform):
|
def get(obj, platform):
|
||||||
|
@ -385,6 +391,19 @@ class IOBufferInstance(Fragment):
|
||||||
self.src_loc = src_loc or tracer.get_src_loc(src_loc_at)
|
self.src_loc = src_loc or tracer.get_src_loc(src_loc_at)
|
||||||
|
|
||||||
|
|
||||||
|
class RequirePosedge(Fragment):
|
||||||
|
"""A special fragment that requires a given domain to have :py:`clk_edge="pos"`, failing
|
||||||
|
elaboration otherwise.
|
||||||
|
|
||||||
|
This is a private interface, without a stability guarantee.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, domain, *, src_loc_at=0, src_loc=None):
|
||||||
|
super().__init__()
|
||||||
|
self._domain = domain
|
||||||
|
self.src_loc = src_loc or tracer.get_src_loc(src_loc_at)
|
||||||
|
|
||||||
|
|
||||||
def _add_name(assigned_names, name):
|
def _add_name(assigned_names, name):
|
||||||
if name in assigned_names:
|
if name in assigned_names:
|
||||||
name = f"{name}${len(assigned_names)}"
|
name = f"{name}${len(assigned_names)}"
|
||||||
|
@ -429,6 +448,7 @@ class Design:
|
||||||
else:
|
else:
|
||||||
self._use_signal(fragment, conn)
|
self._use_signal(fragment, conn)
|
||||||
self._assign_names(fragment, hierarchy)
|
self._assign_names(fragment, hierarchy)
|
||||||
|
self._check_domain_requires()
|
||||||
|
|
||||||
def _compute_fragment_depth_parent(self, fragment: Fragment, parent: "Fragment | None", depth: int):
|
def _compute_fragment_depth_parent(self, fragment: Fragment, parent: "Fragment | None", depth: int):
|
||||||
"""Recursively computes every fragment's depth and parent."""
|
"""Recursively computes every fragment's depth and parent."""
|
||||||
|
@ -576,6 +596,8 @@ class Design:
|
||||||
self._use_signal(fragment, domain.clk)
|
self._use_signal(fragment, domain.clk)
|
||||||
if domain.rst is not None:
|
if domain.rst is not None:
|
||||||
self._use_signal(fragment, domain.rst)
|
self._use_signal(fragment, domain.rst)
|
||||||
|
elif isinstance(fragment, _ir.RequirePosedge):
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
for domain_name, statements in fragment.statements.items():
|
for domain_name, statements in fragment.statements.items():
|
||||||
if domain_name != "comb":
|
if domain_name != "comb":
|
||||||
|
@ -662,6 +684,18 @@ class Design:
|
||||||
subfragment_name = _add_name(frag_info.assigned_names, subfragment_name)
|
subfragment_name = _add_name(frag_info.assigned_names, subfragment_name)
|
||||||
self._assign_names(subfragment, hierarchy=(*hierarchy, subfragment_name))
|
self._assign_names(subfragment, hierarchy=(*hierarchy, subfragment_name))
|
||||||
|
|
||||||
|
def _check_domain_requires(self):
|
||||||
|
for fragment, fragment_info in self.fragments.items():
|
||||||
|
if isinstance(fragment, RequirePosedge):
|
||||||
|
domain = fragment.domains[fragment._domain]
|
||||||
|
if domain.clk_edge != "pos":
|
||||||
|
if fragment.src_loc is None:
|
||||||
|
src_loc = "<unknown>:0"
|
||||||
|
else:
|
||||||
|
src_loc = f"{fragment.src_loc[0]}:{fragment.src_loc[1]}"
|
||||||
|
fragment_name = ".".join(fragment_info.name)
|
||||||
|
raise DomainRequirementFailed(f"Domain {domain.name} has a negedge clock, but posedge clock is required by {fragment_name} at {src_loc}")
|
||||||
|
|
||||||
def lookup_domain(self, domain, context):
|
def lookup_domain(self, domain, context):
|
||||||
if domain == "comb":
|
if domain == "comb":
|
||||||
raise KeyError("comb")
|
raise KeyError("comb")
|
||||||
|
@ -1491,6 +1525,8 @@ class NetlistEmitter:
|
||||||
assert parent_module_idx is not None
|
assert parent_module_idx is not None
|
||||||
self.emit_iobuffer(parent_module_idx, fragment)
|
self.emit_iobuffer(parent_module_idx, fragment)
|
||||||
self.fragment_module_idx[fragment] = parent_module_idx
|
self.fragment_module_idx[fragment] = parent_module_idx
|
||||||
|
elif isinstance(fragment, _ir.RequirePosedge):
|
||||||
|
pass
|
||||||
elif type(fragment) is _ir.Fragment:
|
elif type(fragment) is _ir.Fragment:
|
||||||
module_idx = self.netlist.add_module(parent_module_idx, fragment_name, src_loc=fragment.src_loc, cell_src_loc=cell_src_loc)
|
module_idx = self.netlist.add_module(parent_module_idx, fragment_name, src_loc=fragment.src_loc, cell_src_loc=cell_src_loc)
|
||||||
self.fragment_module_idx[fragment] = module_idx
|
self.fragment_module_idx[fragment] = module_idx
|
||||||
|
|
|
@ -314,6 +314,8 @@ class FragmentTransformer:
|
||||||
oe=fragment.oe,
|
oe=fragment.oe,
|
||||||
src_loc=fragment.src_loc,
|
src_loc=fragment.src_loc,
|
||||||
)
|
)
|
||||||
|
elif isinstance(fragment, RequirePosedge):
|
||||||
|
new_fragment = RequirePosedge(fragment._domain, src_loc=fragment.src_loc)
|
||||||
else:
|
else:
|
||||||
new_fragment = Fragment(src_loc=fragment.src_loc)
|
new_fragment = Fragment(src_loc=fragment.src_loc)
|
||||||
new_fragment.attrs = OrderedDict(fragment.attrs)
|
new_fragment.attrs = OrderedDict(fragment.attrs)
|
||||||
|
@ -445,6 +447,8 @@ class DomainCollector(ValueVisitor, StatementVisitor):
|
||||||
self.on_value(port._data)
|
self.on_value(port._data)
|
||||||
self.on_value(port._en)
|
self.on_value(port._en)
|
||||||
self._add_used_domain(port._domain)
|
self._add_used_domain(port._domain)
|
||||||
|
if isinstance(fragment, RequirePosedge):
|
||||||
|
self._add_used_domain(fragment._domain)
|
||||||
|
|
||||||
if isinstance(fragment, Instance):
|
if isinstance(fragment, Instance):
|
||||||
for name, (value, dir) in fragment.ports.items():
|
for name, (value, dir) in fragment.ports.items():
|
||||||
|
@ -535,6 +539,12 @@ class DomainRenamer(FragmentTransformer, ValueTransformer, StatementTransformer)
|
||||||
if port._domain in self.domain_map:
|
if port._domain in self.domain_map:
|
||||||
port._domain = self.domain_map[port._domain]
|
port._domain = self.domain_map[port._domain]
|
||||||
|
|
||||||
|
def on_fragment(self, fragment):
|
||||||
|
new_fragment = super().on_fragment(fragment)
|
||||||
|
if isinstance(new_fragment, RequirePosedge) and new_fragment._domain in self.domain_map:
|
||||||
|
new_fragment._domain = self.domain_map[new_fragment._domain]
|
||||||
|
return new_fragment
|
||||||
|
|
||||||
|
|
||||||
class DomainLowerer(FragmentTransformer, ValueTransformer, StatementTransformer):
|
class DomainLowerer(FragmentTransformer, ValueTransformer, StatementTransformer):
|
||||||
def __init__(self, domains=None):
|
def __init__(self, domains=None):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from .. import *
|
from .. import *
|
||||||
|
from ..hdl._ir import RequirePosedge
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["FFSynchronizer", "AsyncFFSynchronizer", "ResetSynchronizer", "PulseSynchronizer"]
|
__all__ = ["FFSynchronizer", "AsyncFFSynchronizer", "ResetSynchronizer", "PulseSynchronizer"]
|
||||||
|
@ -187,6 +188,7 @@ class AsyncFFSynchronizer(Elaboratable):
|
||||||
ClockSignal("async_ff").eq(ClockSignal(self._o_domain)),
|
ClockSignal("async_ff").eq(ClockSignal(self._o_domain)),
|
||||||
self.o.eq(flops[-1])
|
self.o.eq(flops[-1])
|
||||||
]
|
]
|
||||||
|
m.submodules += RequirePosedge(self._o_domain)
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from abc import abstractmethod
|
||||||
|
|
||||||
from ..hdl import *
|
from ..hdl import *
|
||||||
from ..hdl import _ast
|
from ..hdl import _ast
|
||||||
|
from ..hdl._ir import RequirePosedge
|
||||||
from ..lib import io, wiring
|
from ..lib import io, wiring
|
||||||
from ..build import *
|
from ..build import *
|
||||||
|
|
||||||
|
@ -152,6 +153,7 @@ class DDRBuffer(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_reg = Signal(len(self.port))
|
i0_reg = Signal(len(self.port))
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
|
@ -167,6 +169,7 @@ class DDRBuffer(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
m.submodules.o_ddr = Instance("altddio_out",
|
m.submodules.o_ddr = Instance("altddio_out",
|
||||||
p_width=len(self.port),
|
p_width=len(self.port),
|
||||||
o_dataout=buf.o,
|
o_dataout=buf.o,
|
||||||
|
@ -507,6 +510,7 @@ class AlteraPlatform(TemplatedPlatform):
|
||||||
def get_async_ff_sync(self, async_ff_sync):
|
def get_async_ff_sync(self, async_ff_sync):
|
||||||
m = Module()
|
m = Module()
|
||||||
sync_output = Signal()
|
sync_output = Signal()
|
||||||
|
m.submodules += RequirePosedge(async_ff_sync._o_domain)
|
||||||
if async_ff_sync._edge == "pos":
|
if async_ff_sync._edge == "pos":
|
||||||
m.submodules += Instance("altera_std_synchronizer",
|
m.submodules += Instance("altera_std_synchronizer",
|
||||||
p_depth=async_ff_sync._stages,
|
p_depth=async_ff_sync._stages,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import math
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ..hdl import *
|
from ..hdl import *
|
||||||
|
from ..hdl._ir import RequirePosedge
|
||||||
from ..lib import io, wiring
|
from ..lib import io, wiring
|
||||||
from ..lib.cdc import ResetSynchronizer
|
from ..lib.cdc import ResetSynchronizer
|
||||||
from ..build import *
|
from ..build import *
|
||||||
|
@ -131,6 +132,7 @@ class DDRBuffer(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -144,6 +146,7 @@ class DDRBuffer(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = self.o[0] ^ inv_mask
|
o0_inv = self.o[0] ^ inv_mask
|
||||||
o1_inv = self.o[1] ^ inv_mask
|
o1_inv = self.o[1] ^ inv_mask
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from ..hdl import *
|
from ..hdl import *
|
||||||
|
from ..hdl._ir import RequirePosedge
|
||||||
from ..lib import io, wiring
|
from ..lib import io, wiring
|
||||||
from ..build import *
|
from ..build import *
|
||||||
|
|
||||||
|
@ -107,6 +108,7 @@ class FFBufferECP5(io.FFBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i_inv = Signal.like(self.i)
|
i_inv = Signal.like(self.i)
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
m.submodules[f"i_ff{bit}"] = Instance("IFS1P3DX",
|
m.submodules[f"i_ff{bit}"] = Instance("IFS1P3DX",
|
||||||
|
@ -119,6 +121,7 @@ class FFBufferECP5(io.FFBuffer):
|
||||||
m.d.comb += self.i.eq(i_inv ^ inv_mask)
|
m.d.comb += self.i.eq(i_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o_inv = Signal.like(self.o)
|
o_inv = Signal.like(self.o)
|
||||||
m.d.comb += o_inv.eq(self.o ^ inv_mask)
|
m.d.comb += o_inv.eq(self.o ^ inv_mask)
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -142,6 +145,7 @@ class FFBufferNexus(io.FFBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i_inv = Signal.like(self.i)
|
i_inv = Signal.like(self.i)
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
m.submodules[f"i_ff{bit}"] = Instance("IFD1P3DX",
|
m.submodules[f"i_ff{bit}"] = Instance("IFD1P3DX",
|
||||||
|
@ -154,6 +158,7 @@ class FFBufferNexus(io.FFBuffer):
|
||||||
m.d.comb += self.i.eq(i_inv ^ inv_mask)
|
m.d.comb += self.i.eq(i_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o_inv = Signal.like(self.o)
|
o_inv = Signal.like(self.o)
|
||||||
m.d.comb += o_inv.eq(self.o ^ inv_mask)
|
m.d.comb += o_inv.eq(self.o ^ inv_mask)
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -177,6 +182,7 @@ class DDRBufferECP5(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -191,6 +197,7 @@ class DDRBufferECP5(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = Signal(len(self.port))
|
o0_inv = Signal(len(self.port))
|
||||||
o1_inv = Signal(len(self.port))
|
o1_inv = Signal(len(self.port))
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
|
@ -218,6 +225,7 @@ class DDRBufferMachXO2(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -232,6 +240,7 @@ class DDRBufferMachXO2(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = Signal(len(self.port))
|
o0_inv = Signal(len(self.port))
|
||||||
o1_inv = Signal(len(self.port))
|
o1_inv = Signal(len(self.port))
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
|
@ -259,6 +268,7 @@ class DDRBufferNexus(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -273,6 +283,7 @@ class DDRBufferNexus(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = Signal(len(self.port))
|
o0_inv = Signal(len(self.port))
|
||||||
o1_inv = Signal(len(self.port))
|
o1_inv = Signal(len(self.port))
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from ..hdl import *
|
from ..hdl import *
|
||||||
|
from ..hdl._ir import RequirePosedge
|
||||||
from ..lib.cdc import ResetSynchronizer
|
from ..lib.cdc import ResetSynchronizer
|
||||||
from ..lib import io
|
from ..lib import io
|
||||||
from ..build import *
|
from ..build import *
|
||||||
|
@ -506,10 +507,12 @@ class SiliconBluePlatform(TemplatedPlatform):
|
||||||
else:
|
else:
|
||||||
io_args.append(("o", "D_IN_0", i[bit]))
|
io_args.append(("o", "D_IN_0", i[bit]))
|
||||||
elif isinstance(buffer, io.FFBuffer):
|
elif isinstance(buffer, io.FFBuffer):
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i_type = 0b00 # PIN_INPUT_REGISTERED aka PIN_INPUT_DDR
|
i_type = 0b00 # PIN_INPUT_REGISTERED aka PIN_INPUT_DDR
|
||||||
io_args.append(("i", "INPUT_CLK", ClockSignal(buffer.i_domain)))
|
io_args.append(("i", "INPUT_CLK", ClockSignal(buffer.i_domain)))
|
||||||
io_args.append(("o", "D_IN_0", i[bit]))
|
io_args.append(("o", "D_IN_0", i[bit]))
|
||||||
elif isinstance(buffer, io.DDRBuffer):
|
elif isinstance(buffer, io.DDRBuffer):
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i_type = 0b00 # PIN_INPUT_REGISTERED aka PIN_INPUT_DDR
|
i_type = 0b00 # PIN_INPUT_REGISTERED aka PIN_INPUT_DDR
|
||||||
io_args.append(("i", "INPUT_CLK", ClockSignal(buffer.i_domain)))
|
io_args.append(("i", "INPUT_CLK", ClockSignal(buffer.i_domain)))
|
||||||
io_args.append(("o", "D_IN_0", i0[bit]))
|
io_args.append(("o", "D_IN_0", i0[bit]))
|
||||||
|
@ -521,10 +524,12 @@ class SiliconBluePlatform(TemplatedPlatform):
|
||||||
o_type = 0b1010 # PIN_OUTPUT_TRISTATE
|
o_type = 0b1010 # PIN_OUTPUT_TRISTATE
|
||||||
io_args.append(("i", "D_OUT_0", o[bit]))
|
io_args.append(("i", "D_OUT_0", o[bit]))
|
||||||
elif isinstance(buffer, io.FFBuffer):
|
elif isinstance(buffer, io.FFBuffer):
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o_type = 0b1101 # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED
|
o_type = 0b1101 # PIN_OUTPUT_REGISTERED_ENABLE_REGISTERED
|
||||||
io_args.append(("i", "OUTPUT_CLK", ClockSignal(buffer.o_domain)))
|
io_args.append(("i", "OUTPUT_CLK", ClockSignal(buffer.o_domain)))
|
||||||
io_args.append(("i", "D_OUT_0", o[bit]))
|
io_args.append(("i", "D_OUT_0", o[bit]))
|
||||||
elif isinstance(buffer, io.DDRBuffer):
|
elif isinstance(buffer, io.DDRBuffer):
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED
|
o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED
|
||||||
io_args.append(("i", "OUTPUT_CLK", ClockSignal(buffer.o_domain)))
|
io_args.append(("i", "OUTPUT_CLK", ClockSignal(buffer.o_domain)))
|
||||||
io_args.append(("i", "D_OUT_0", o0[bit]))
|
io_args.append(("i", "D_OUT_0", o0[bit]))
|
||||||
|
|
|
@ -2,6 +2,7 @@ import re
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
|
|
||||||
from ..hdl import *
|
from ..hdl import *
|
||||||
|
from ..hdl._ir import RequirePosedge
|
||||||
from ..lib.cdc import ResetSynchronizer
|
from ..lib.cdc import ResetSynchronizer
|
||||||
from ..lib import io, wiring
|
from ..lib import io, wiring
|
||||||
from ..build import *
|
from ..build import *
|
||||||
|
@ -125,11 +126,13 @@ class FFBuffer(io.FFBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i_inv = Signal.like(self.i)
|
i_inv = Signal.like(self.i)
|
||||||
_make_dff(m, "i", self.i_domain, buf.i, i_inv, iob=True)
|
_make_dff(m, "i", self.i_domain, buf.i, i_inv, iob=True)
|
||||||
m.d.comb += self.i.eq(i_inv ^ inv_mask)
|
m.d.comb += self.i.eq(i_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o_inv = Signal.like(self.o)
|
o_inv = Signal.like(self.o)
|
||||||
m.d.comb += o_inv.eq(self.o ^ inv_mask)
|
m.d.comb += o_inv.eq(self.o ^ inv_mask)
|
||||||
_make_dff(m, "o", self.o_domain, o_inv, buf.o, iob=True)
|
_make_dff(m, "o", self.o_domain, o_inv, buf.o, iob=True)
|
||||||
|
@ -146,6 +149,7 @@ class DDRBufferVirtex2(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
# First-generation input DDR register: basically just two FFs with opposite
|
# First-generation input DDR register: basically just two FFs with opposite
|
||||||
|
@ -161,6 +165,7 @@ class DDRBufferVirtex2(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = Signal(len(self.port))
|
o0_inv = Signal(len(self.port))
|
||||||
o1_inv = Signal(len(self.port))
|
o1_inv = Signal(len(self.port))
|
||||||
o1_ff = Signal(len(self.port))
|
o1_ff = Signal(len(self.port))
|
||||||
|
@ -210,6 +215,7 @@ class DDRBufferSpartan3E(io.DDRBuffer):
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
if platform.family == "spartan6" or isinstance(self.port, io.DifferentialPort):
|
if platform.family == "spartan6" or isinstance(self.port, io.DifferentialPort):
|
||||||
|
@ -257,6 +263,7 @@ class DDRBufferSpartan3E(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = Signal(len(self.port))
|
o0_inv = Signal(len(self.port))
|
||||||
o1_inv = Signal(len(self.port))
|
o1_inv = Signal(len(self.port))
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
|
@ -333,6 +340,7 @@ class DDRBufferVirtex4(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -352,6 +360,7 @@ class DDRBufferVirtex4(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = Signal(len(self.port))
|
o0_inv = Signal(len(self.port))
|
||||||
o1_inv = Signal(len(self.port))
|
o1_inv = Signal(len(self.port))
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
|
@ -384,6 +393,7 @@ class DDRBufferUltrascale(io.DDRBuffer):
|
||||||
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
|
||||||
|
|
||||||
if self.direction is not io.Direction.Output:
|
if self.direction is not io.Direction.Output:
|
||||||
|
m.submodules += RequirePosedge(self.i_domain)
|
||||||
i0_inv = Signal(len(self.port))
|
i0_inv = Signal(len(self.port))
|
||||||
i1_inv = Signal(len(self.port))
|
i1_inv = Signal(len(self.port))
|
||||||
for bit in range(len(self.port)):
|
for bit in range(len(self.port)):
|
||||||
|
@ -402,6 +412,7 @@ class DDRBufferUltrascale(io.DDRBuffer):
|
||||||
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
|
||||||
|
|
||||||
if self.direction is not io.Direction.Input:
|
if self.direction is not io.Direction.Input:
|
||||||
|
m.submodules += RequirePosedge(self.o_domain)
|
||||||
o0_inv = Signal(len(self.port))
|
o0_inv = Signal(len(self.port))
|
||||||
o1_inv = Signal(len(self.port))
|
o1_inv = Signal(len(self.port))
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
|
@ -1234,6 +1245,7 @@ class XilinxPlatform(TemplatedPlatform):
|
||||||
|
|
||||||
def get_async_ff_sync(self, async_ff_sync):
|
def get_async_ff_sync(self, async_ff_sync):
|
||||||
m = Module()
|
m = Module()
|
||||||
|
m.submodules += RequirePosedge(async_ff_sync._o_domain)
|
||||||
m.domains += ClockDomain("async_ff", async_reset=True, local=True)
|
m.domains += ClockDomain("async_ff", async_reset=True, local=True)
|
||||||
# Instantiate a chain of async_ff_sync._stages FDPEs with all
|
# Instantiate a chain of async_ff_sync._stages FDPEs with all
|
||||||
# their PRE pins connected to either async_ff_sync.i or
|
# their PRE pins connected to either async_ff_sync.i or
|
||||||
|
|
|
@ -3598,3 +3598,28 @@ class DomainLookupTestCase(FHDLTestCase):
|
||||||
self.assertIs(design.lookup_domain("sync", xm5), m1_c)
|
self.assertIs(design.lookup_domain("sync", xm5), m1_c)
|
||||||
self.assertIs(design.lookup_domain("d", xm4), m4_d)
|
self.assertIs(design.lookup_domain("d", xm4), m4_d)
|
||||||
self.assertIs(design.lookup_domain("d", xm5), m5_d)
|
self.assertIs(design.lookup_domain("d", xm5), m5_d)
|
||||||
|
|
||||||
|
|
||||||
|
class RequirePosedgeTestCase(FHDLTestCase):
|
||||||
|
def test_require_ok(self):
|
||||||
|
m = Module()
|
||||||
|
m.domains.sync = ClockDomain()
|
||||||
|
m.submodules += RequirePosedge("sync")
|
||||||
|
Fragment.get(m, None).prepare()
|
||||||
|
|
||||||
|
def test_require_fail(self):
|
||||||
|
m = Module()
|
||||||
|
m.domains.sync = ClockDomain(clk_edge="neg")
|
||||||
|
m.submodules += RequirePosedge("sync")
|
||||||
|
with self.assertRaisesRegex(DomainRequirementFailed,
|
||||||
|
r"^Domain sync has a negedge clock, but posedge clock is required by top.U\$0 at .*$"):
|
||||||
|
Fragment.get(m, None).prepare()
|
||||||
|
|
||||||
|
def test_require_renamed(self):
|
||||||
|
m = Module()
|
||||||
|
m.domains.sync = ClockDomain(clk_edge="pos")
|
||||||
|
m.domains.test = ClockDomain(clk_edge="neg")
|
||||||
|
m.submodules += DomainRenamer("test")(RequirePosedge("sync"))
|
||||||
|
with self.assertRaisesRegex(DomainRequirementFailed,
|
||||||
|
r"^Domain test has a negedge clock, but posedge clock is required by top.U\$0 at .*$"):
|
||||||
|
Fragment.get(m, None).prepare()
|
Loading…
Reference in a new issue