Skip to content

build: add clock constraints on IOPort instead of buffered Signal. #1441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions amaranth/build/plat.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,14 @@ class TemplatedPlatform(Platform):
""",
}

def iter_clock_constraints(self):
for net_signal, port_signal, frequency in super().iter_clock_constraints():
def iter_signal_clock_constraints(self):
for signal, frequency in super().iter_signal_clock_constraints():
# Skip any clock constraints placed on signals that are never used in the design.
# Otherwise, it will cause a crash in the vendor platform if it supports clock
# constraints on non-port nets.
if net_signal not in self._name_map:
if signal not in self._name_map:
continue
yield net_signal, port_signal, frequency
yield signal, frequency

def toolchain_prepare(self, fragment, name, *, emit_src=True, **kwargs):
# Restrict the name of the design to a strict alphanumeric character set. Platforms will
Expand Down Expand Up @@ -327,8 +327,11 @@ def options(opts):
else:
return " ".join(opts)

def hierarchy(signal, separator):
return separator.join(self._name_map[signal][1:])
def hierarchy(net, separator):
if isinstance(net, IOPort):
return net.name
else:
return separator.join(self._name_map[net][1:])

def ascii_escape(string):
def escape_one(match):
Expand Down
56 changes: 24 additions & 32 deletions amaranth/build/res.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ def __init__(self, resources, connectors):

# List of (pin, port, buffer) pairs for non-dir="-" requests.
self._pins = []
# Constraint list
# Constraint lists
self._clocks = SignalDict()
self._io_clocks = {}

self.add_resources(resources)
self.add_connectors(connectors)
Expand Down Expand Up @@ -219,6 +220,8 @@ def resolve(resource, dir, xdr, path, attrs):
for name in phys_names
])
port = io.SingleEndedPort(iop, invert=phys.invert, direction=direction)
if resource.clock is not None:
self.add_clock_constraint(iop, resource.clock.frequency)
if isinstance(phys, DiffPairs):
phys_names_p = phys.p.map_names(self._conn_pins, resource)
phys_names_n = phys.n.map_names(self._conn_pins, resource)
Expand All @@ -232,7 +235,8 @@ def resolve(resource, dir, xdr, path, attrs):
for name in phys_names_n
])
port = io.DifferentialPort(p, n, invert=phys.invert, direction=direction)

if resource.clock is not None:
self.add_clock_constraint(p, resource.clock.frequency)
for phys_name in phys_names:
if phys_name in self._phys_reqd:
raise ResourceError("Resource component {} uses physical pin {}, but it "
Expand All @@ -253,8 +257,6 @@ def resolve(resource, dir, xdr, path, attrs):
buffer = PinBuffer(pin, port)
self._pins.append((pin, port, buffer))

if resource.clock is not None:
self.add_clock_constraint(pin.i, resource.clock.frequency)
return pin

else:
Expand All @@ -275,38 +277,28 @@ def add_clock_constraint(self, clock, frequency):
raise TypeError(f"A clock constraint can only be applied to a Signal, but a "
f"ClockSignal is provided; assign the ClockSignal to an "
f"intermediate signal and constrain the latter instead.")
elif not isinstance(clock, Signal):
raise TypeError(f"Object {clock!r} is not a Signal")
elif not isinstance(clock, (Signal, IOPort)):
raise TypeError(f"Object {clock!r} is not a Signal or IOPort")
if not isinstance(frequency, (int, float)):
raise TypeError(f"Frequency must be a number, not {frequency!r}")

if clock in self._clocks:
if isinstance(clock, IOPort):
clocks = self._io_clocks
else:
clocks = self._clocks

frequency = float(frequency)
if clock in clocks and clocks[clock] != frequency:
raise ValueError("Cannot add clock constraint on {!r}, which is already constrained "
"to {} Hz"
.format(clock, self._clocks[clock]))
.format(clock, clocks[clock]))
else:
self._clocks[clock] = float(frequency)

def iter_clock_constraints(self):
# Back-propagate constraints through the input buffer. For clock constraints on pins
# (the majority of cases), toolchains work better if the constraint is defined on the pin
# and not on the buffered internal net; and if the toolchain is advanced enough that
# it considers clock phase and delay of the input buffer, it is *necessary* to define
# the constraint on the pin to match the designer's expectation of phase being referenced
# to the pin.
#
# Constraints on nets with no corresponding input pin (e.g. PLL or SERDES outputs) are not
# affected.
pin_i_to_port = SignalDict()
for pin, port, _fragment in self._pins:
if hasattr(pin, "i"):
if isinstance(port, io.SingleEndedPort):
pin_i_to_port[pin.i] = port.io
elif isinstance(port, io.DifferentialPort):
pin_i_to_port[pin.i] = port.p
else:
assert False
clocks[clock] = frequency

def iter_signal_clock_constraints(self):
for signal, frequency in self._clocks.items():
yield signal, frequency

for net_signal, frequency in self._clocks.items():
port_signal = pin_i_to_port.get(net_signal)
yield net_signal, port_signal, frequency
def iter_port_clock_constraints(self):
for port, frequency in self._io_clocks.items():
yield port, frequency
11 changes: 5 additions & 6 deletions amaranth/vendor/_altera.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,11 @@ class AlteraPlatform(TemplatedPlatform):
{{get_override("add_settings")|default("# (add_settings placeholder)")}}
""",
"{{name}}.sdc": r"""
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
{% else -%}
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("|")|tcl_quote}}]
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -name {{signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("|")|tcl_quote}}]
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -name {{port.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port.name|tcl_quote}}]
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
Expand Down
7 changes: 5 additions & 2 deletions amaranth/vendor/_gowin.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,11 @@ def _osc_div(self):
""",
"{{name}}.sdc": r"""
// {{autogenerated}}
{% for net_signal,port_signal,frequency in platform.iter_clock_constraints() -%}
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -name {{signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")|tcl_quote}}]
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -name {{port.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port.name|tcl_quote}}]
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
Expand Down
44 changes: 20 additions & 24 deletions amaranth/vendor/_lattice.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,12 +497,11 @@ class LatticePlatform(TemplatedPlatform):
{%- for key, value in attrs.items() %} {{key}}={{value}}{% endfor %};
{% endif %}
{% endfor %}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
FREQUENCY PORT "{{port_signal.name}}" {{frequency}} HZ;
{% else -%}
FREQUENCY NET "{{net_signal|hierarchy(".")}}" {{frequency}} HZ;
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
FREQUENCY NET "{{signals|hierarchy(".")}}" {{frequency}} HZ;
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
FREQUENCY PORT "{{port.name}}" {{frequency}} HZ;
{% endfor %}
{{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
"""
Expand Down Expand Up @@ -584,12 +583,11 @@ class LatticePlatform(TemplatedPlatform):
ldc_set_port -iobuf {{ '{' }}{%- for key, value in attrs.items() %}{{key}}={{value}} {% endfor %}{{ '}' }} {{'['}}get_ports {{port_name}}{{']'}}
{% endif %}
{% endfor %}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
{% else -%}
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote}}]
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -name {{signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")|tcl_quote}}]
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -name {{port.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port.name|tcl_quote}}]
{% endfor %}
{{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
"""
Expand Down Expand Up @@ -684,12 +682,11 @@ class LatticePlatform(TemplatedPlatform):
""",
"{{name}}.sdc": r"""
set_hierarchy_separator {/}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote("Diamond")}}]
{% else -%}
create_clock -name {{net_signal.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote("Diamond")}}]
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -name {{signal.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")|tcl_quote("Diamond")}}]
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -name {{port.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_ports {{port.name|tcl_quote("Diamond")}}]
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
Expand Down Expand Up @@ -782,12 +779,11 @@ class LatticePlatform(TemplatedPlatform):
""",
# Pre-synthesis SDC constraints
"{{name}}.sdc": r"""
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}]
{% else -%}
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}]
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -name {{signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")|tcl_quote}}]
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -name {{port.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port.name|tcl_quote}}]
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
Expand Down
9 changes: 5 additions & 4 deletions amaranth/vendor/_quicklogic.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ class QuicklogicPlatform(TemplatedPlatform):
""",
"{{name}}.sdc": r"""
# {{autogenerated}}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -period {{100000000/frequency}} {{port_signal.name|ascii_escape}}
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -period {{100000000/frequency}} {{signal.name|ascii_escape}}
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -period {{100000000/frequency}} {{port.name|ascii_escape}}
{% endfor %}
"""
}
Expand Down
20 changes: 11 additions & 9 deletions amaranth/vendor/_siliconblue.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,12 @@ class SiliconBluePlatform(TemplatedPlatform):
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
set_io {{port_name}} {{pin_name}}
{% endfor %}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
set_frequency {{net_signal|hierarchy(".")}} {{frequency/1000000}}
{% endfor%}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
set_frequency {{signal|hierarchy(".")}} {{frequency/1000000}}
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
set_frequency {{port.name}} {{frequency/1000000}}
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
}
Expand Down Expand Up @@ -242,12 +245,11 @@ class SiliconBluePlatform(TemplatedPlatform):
"{{name}}.sdc": r"""
# {{autogenerated}}
set_hierarchy_separator {/}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
{% else -%}
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote}}]
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -name {{signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")|tcl_quote}}]
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -name {{port.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port.name|tcl_quote}}]
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
Expand Down
33 changes: 19 additions & 14 deletions amaranth/vendor/_xilinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,12 +647,11 @@ def vendor_toolchain(self):
set_property {{attr_name}} {{attr_value|tcl_quote}} [get_ports {{port_name|tcl_quote}}]
{% endfor %}
{% endfor %}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
{% else -%}
create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote}}]
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -name {{signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")|tcl_quote}}]
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -name {{port.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{port|hierarchy("/")|tcl_quote}}]
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
"""
Expand Down Expand Up @@ -723,9 +722,13 @@ def vendor_toolchain(self):
NET "{{port_name}}" {{attr_name}}={{attr_value}};
{% endfor %}
{% endfor %}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
NET "{{net_signal|hierarchy("/")}}" TNM_NET="PRD{{net_signal|hierarchy("/")}}";
TIMESPEC "TS{{net_signal|hierarchy("__")}}"=PERIOD "PRD{{net_signal|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
NET "{{signal|hierarchy("/")}}" TNM_NET="PRD{{signal|hierarchy("/")}}";
TIMESPEC "TS{{signal|hierarchy("__")}}"=PERIOD "PRD{{signal|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
NET "{{port|hierarchy("/")}}" TNM_NET="PRD{{port|hierarchy("/")}}";
TIMESPEC "TS{{port|hierarchy("__")}}"=PERIOD "PRD{{port|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
"""
Expand Down Expand Up @@ -848,10 +851,11 @@ def _symbiflow_device(self):
""",
"{{name}}.sdc": r"""
# {{autogenerated}}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is none -%}
create_clock -period {{1000000000/frequency}} {{net_signal.name|ascii_escape}}
{% endif %}
{% for signal, frequency in platform.iter_signal_clock_constraints() -%}
create_clock -period {{1000000000/frequency}} {{signal.name|ascii_escape}}
{% endfor %}
{% for port, frequency in platform.iter_port_clock_constraints() -%}
create_clock -period {{1000000000/frequency}} {{port.name|ascii_escape}}
{% endfor %}
"""
}
Expand Down Expand Up @@ -1173,7 +1177,8 @@ def create_missing_domain(self, name):

def add_clock_constraint(self, clock, frequency):
super().add_clock_constraint(clock, frequency)
clock.attrs["keep"] = "TRUE"
if not isinstance(clock, IOPort):
clock.attrs["keep"] = "TRUE"

def get_io_buffer(self, buffer):
if isinstance(buffer, io.Buffer):
Expand Down
Loading