Skip to content

Commit 2201d80

Browse files
committed
build: add clock constraints on IOPort instead of buffered Signal.
1 parent ef5c506 commit 2201d80

File tree

9 files changed

+79
-91
lines changed

9 files changed

+79
-91
lines changed

amaranth/build/plat.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,13 @@ class TemplatedPlatform(Platform):
199199
}
200200

201201
def iter_clock_constraints(self):
202-
for net_signal, port_signal, frequency in super().iter_clock_constraints():
202+
for net, frequency in super().iter_clock_constraints():
203203
# Skip any clock constraints placed on signals that are never used in the design.
204204
# Otherwise, it will cause a crash in the vendor platform if it supports clock
205205
# constraints on non-port nets.
206-
if net_signal not in self._name_map:
206+
if isinstance(net, Signal) and net not in self._name_map:
207207
continue
208-
yield net_signal, port_signal, frequency
208+
yield net, frequency
209209

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

330-
def hierarchy(signal, separator):
331-
return separator.join(self._name_map[signal][1:])
330+
def hierarchy(net, separator):
331+
if isinstance(net, IOPort):
332+
return net.name
333+
else:
334+
return separator.join(self._name_map[net][1:])
332335

333336
def ascii_escape(string):
334337
def escape_one(match):

amaranth/build/res.py

+21-31
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ def __init__(self, resources, connectors):
109109

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

115116
self.add_resources(resources)
116117
self.add_connectors(connectors)
@@ -219,6 +220,8 @@ def resolve(resource, dir, xdr, path, attrs):
219220
for name in phys_names
220221
])
221222
port = io.SingleEndedPort(iop, invert=phys.invert, direction=direction)
223+
if resource.clock is not None:
224+
self.add_clock_constraint(iop, resource.clock.frequency)
222225
if isinstance(phys, DiffPairs):
223226
phys_names_p = phys.p.map_names(self._conn_pins, resource)
224227
phys_names_n = phys.n.map_names(self._conn_pins, resource)
@@ -232,7 +235,8 @@ def resolve(resource, dir, xdr, path, attrs):
232235
for name in phys_names_n
233236
])
234237
port = io.DifferentialPort(p, n, invert=phys.invert, direction=direction)
235-
238+
if resource.clock is not None:
239+
self.add_clock_constraint(p, resource.clock.frequency)
236240
for phys_name in phys_names:
237241
if phys_name in self._phys_reqd:
238242
raise ResourceError("Resource component {} uses physical pin {}, but it "
@@ -253,8 +257,6 @@ def resolve(resource, dir, xdr, path, attrs):
253257
buffer = PinBuffer(pin, port)
254258
self._pins.append((pin, port, buffer))
255259

256-
if resource.clock is not None:
257-
self.add_clock_constraint(pin.i, resource.clock.frequency)
258260
return pin
259261

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

283-
if clock in self._clocks:
285+
if isinstance(clock, IOPort):
286+
clocks = self._io_clocks
287+
else:
288+
clocks = self._clocks
289+
290+
frequency = float(frequency)
291+
if clock in clocks and clocks[clock] != frequency:
284292
raise ValueError("Cannot add clock constraint on {!r}, which is already constrained "
285293
"to {} Hz"
286-
.format(clock, self._clocks[clock]))
294+
.format(clock, clocks[clock]))
287295
else:
288-
self._clocks[clock] = float(frequency)
296+
clocks[clock] = frequency
289297

290298
def iter_clock_constraints(self):
291-
# Back-propagate constraints through the input buffer. For clock constraints on pins
292-
# (the majority of cases), toolchains work better if the constraint is defined on the pin
293-
# and not on the buffered internal net; and if the toolchain is advanced enough that
294-
# it considers clock phase and delay of the input buffer, it is *necessary* to define
295-
# the constraint on the pin to match the designer's expectation of phase being referenced
296-
# to the pin.
297-
#
298-
# Constraints on nets with no corresponding input pin (e.g. PLL or SERDES outputs) are not
299-
# affected.
300-
pin_i_to_port = SignalDict()
301-
for pin, port, _fragment in self._pins:
302-
if hasattr(pin, "i"):
303-
if isinstance(port, io.SingleEndedPort):
304-
pin_i_to_port[pin.i] = port.io
305-
elif isinstance(port, io.DifferentialPort):
306-
pin_i_to_port[pin.i] = port.p
307-
else:
308-
assert False
309-
310-
for net_signal, frequency in self._clocks.items():
311-
port_signal = pin_i_to_port.get(net_signal)
312-
yield net_signal, port_signal, frequency
299+
for net, frequency in self._clocks.items():
300+
yield net, frequency
301+
for net, frequency in self._io_clocks.items():
302+
yield net, frequency

amaranth/vendor/_altera.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,11 @@ class AlteraPlatform(TemplatedPlatform):
310310
{{get_override("add_settings")|default("# (add_settings placeholder)")}}
311311
""",
312312
"{{name}}.sdc": r"""
313-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
314-
{% if port_signal is not none -%}
315-
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
313+
{% for net, frequency in platform.iter_clock_constraints() -%}
314+
{% if net.__class__.__name__ == "IOPort" -%}
315+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{net.name|tcl_quote}}]
316316
{% else -%}
317-
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("|")|tcl_quote}}]
317+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net|hierarchy("|")|tcl_quote}}]
318318
{% endif %}
319319
{% endfor %}
320320
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}

amaranth/vendor/_gowin.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,12 @@ def _osc_div(self):
439439
""",
440440
"{{name}}.sdc": r"""
441441
// {{autogenerated}}
442-
{% for net_signal,port_signal,frequency in platform.iter_clock_constraints() -%}
443-
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
442+
{% for net, frequency in platform.iter_clock_constraints() -%}
443+
{% if net.__class__.__name__ == "IOPort" -%}
444+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{net.name|tcl_quote}}]
445+
{% else -%}
446+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net|hierarchy("/")|tcl_quote}}]
447+
{% endif %}
444448
{% endfor %}
445449
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
446450
""",

amaranth/vendor/_lattice.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -497,11 +497,11 @@ class LatticePlatform(TemplatedPlatform):
497497
{%- for key, value in attrs.items() %} {{key}}={{value}}{% endfor %};
498498
{% endif %}
499499
{% endfor %}
500-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
501-
{% if port_signal is not none -%}
502-
FREQUENCY PORT "{{port_signal.name}}" {{frequency}} HZ;
500+
{% for net, frequency in platform.iter_clock_constraints() -%}
501+
{% if net.__class__.__name__ == "IOPort" -%}
502+
FREQUENCY PORT "{{net.name}}" {{frequency}} HZ;
503503
{% else -%}
504-
FREQUENCY NET "{{net_signal|hierarchy(".")}}" {{frequency}} HZ;
504+
FREQUENCY NET "{{net|hierarchy(".")}}" {{frequency}} HZ;
505505
{% endif %}
506506
{% endfor %}
507507
{{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
@@ -584,11 +584,11 @@ class LatticePlatform(TemplatedPlatform):
584584
ldc_set_port -iobuf {{ '{' }}{%- for key, value in attrs.items() %}{{key}}={{value}} {% endfor %}{{ '}' }} {{'['}}get_ports {{port_name}}{{']'}}
585585
{% endif %}
586586
{% endfor %}
587-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
588-
{% if port_signal is not none -%}
589-
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
587+
{% for net, frequency in platform.iter_clock_constraints() -%}
588+
{% if net.__class__.__name__ == "IOPort" -%}
589+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{net.name|tcl_quote}}]
590590
{% else -%}
591-
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote}}]
591+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net|hierarchy("/")|tcl_quote}}]
592592
{% endif %}
593593
{% endfor %}
594594
{{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
@@ -684,11 +684,11 @@ class LatticePlatform(TemplatedPlatform):
684684
""",
685685
"{{name}}.sdc": r"""
686686
set_hierarchy_separator {/}
687-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
688-
{% if port_signal is not none -%}
689-
create_clock -name {{port_signal.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote("Diamond")}}]
687+
{% for net, frequency in platform.iter_clock_constraints() -%}
688+
{% if net.__class__.__name__ == "IOPort" -%}
689+
create_clock -name {{net.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_ports {{net.name|tcl_quote("Diamond")}}]
690690
{% else -%}
691-
create_clock -name {{net_signal.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote("Diamond")}}]
691+
create_clock -name {{net.name|tcl_quote("Diamond")}} -period {{1000000000/frequency}} [get_nets {{net|hierarchy("/")|tcl_quote("Diamond")}}]
692692
{% endif %}
693693
{% endfor %}
694694
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
@@ -782,11 +782,11 @@ class LatticePlatform(TemplatedPlatform):
782782
""",
783783
# Pre-synthesis SDC constraints
784784
"{{name}}.sdc": r"""
785-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
786-
{% if port_signal is not none -%}
787-
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}]
785+
{% for net, frequency in platform.iter_clock_constraints() -%}
786+
{% if net.__class__.__name__ == "IOPort" -%}
787+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{net.name}}]
788788
{% else -%}
789-
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}]
789+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net|hierarchy("/")}}]
790790
{% endif %}
791791
{% endfor %}
792792
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}

amaranth/vendor/_quicklogic.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,8 @@ class QuicklogicPlatform(TemplatedPlatform):
7171
""",
7272
"{{name}}.sdc": r"""
7373
# {{autogenerated}}
74-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
75-
{% if port_signal is not none -%}
76-
create_clock -period {{100000000/frequency}} {{port_signal.name|ascii_escape}}
77-
{% endif %}
74+
{% for net, frequency in platform.iter_clock_constraints() -%}
75+
create_clock -period {{100000000/frequency}} {{net.name|ascii_escape}}
7876
{% endfor %}
7977
"""
8078
}

amaranth/vendor/_siliconblue.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ class SiliconBluePlatform(TemplatedPlatform):
134134
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
135135
set_io {{port_name}} {{pin_name}}
136136
{% endfor %}
137-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
138-
set_frequency {{net_signal|hierarchy(".")}} {{frequency/1000000}}
137+
{% for net, frequency in platform.iter_clock_constraints() -%}
138+
set_frequency {{net|hierarchy(".")}} {{frequency/1000000}}
139139
{% endfor%}
140140
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
141141
""",
@@ -242,11 +242,11 @@ class SiliconBluePlatform(TemplatedPlatform):
242242
"{{name}}.sdc": r"""
243243
# {{autogenerated}}
244244
set_hierarchy_separator {/}
245-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
246-
{% if port_signal is not none -%}
247-
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
245+
{% for net, frequency in platform.iter_clock_constraints() -%}
246+
{% if net.__class__.__name__ == "IOPort" -%}
247+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{net.name|tcl_quote}}]
248248
{% else -%}
249-
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote}}]
249+
create_clock -name {{net.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net|hierarchy("/")|tcl_quote}}]
250250
{% endif %}
251251
{% endfor %}
252252
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}

amaranth/vendor/_xilinx.py

+9-14
Original file line numberDiff line numberDiff line change
@@ -647,12 +647,8 @@ def vendor_toolchain(self):
647647
set_property {{attr_name}} {{attr_value|tcl_quote}} [get_ports {{port_name|tcl_quote}}]
648648
{% endfor %}
649649
{% endfor %}
650-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
651-
{% if port_signal is not none -%}
652-
create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
653-
{% else -%}
654-
create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote}}]
655-
{% endif %}
650+
{% for net, frequency in platform.iter_clock_constraints() -%}
651+
create_clock -name {{net.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net|hierarchy("/")|tcl_quote}}]
656652
{% endfor %}
657653
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
658654
"""
@@ -723,9 +719,9 @@ def vendor_toolchain(self):
723719
NET "{{port_name}}" {{attr_name}}={{attr_value}};
724720
{% endfor %}
725721
{% endfor %}
726-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
727-
NET "{{net_signal|hierarchy("/")}}" TNM_NET="PRD{{net_signal|hierarchy("/")}}";
728-
TIMESPEC "TS{{net_signal|hierarchy("__")}}"=PERIOD "PRD{{net_signal|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
722+
{% for net, frequency in platform.iter_clock_constraints() -%}
723+
NET "{{net|hierarchy("/")}}" TNM_NET="PRD{{net|hierarchy("/")}}";
724+
TIMESPEC "TS{{net|hierarchy("__")}}"=PERIOD "PRD{{net|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%;
729725
{% endfor %}
730726
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
731727
"""
@@ -848,10 +844,8 @@ def _symbiflow_device(self):
848844
""",
849845
"{{name}}.sdc": r"""
850846
# {{autogenerated}}
851-
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
852-
{% if port_signal is none -%}
853-
create_clock -period {{1000000000/frequency}} {{net_signal.name|ascii_escape}}
854-
{% endif %}
847+
{% for net, frequency in platform.iter_clock_constraints() -%}
848+
create_clock -period {{1000000000/frequency}} {{net.name|ascii_escape}}
855849
{% endfor %}
856850
"""
857851
}
@@ -1173,7 +1167,8 @@ def create_missing_domain(self, name):
11731167

11741168
def add_clock_constraint(self, clock, frequency):
11751169
super().add_clock_constraint(clock, frequency)
1176-
clock.attrs["keep"] = "TRUE"
1170+
if not isinstance(clock, IOPort):
1171+
clock.attrs["keep"] = "TRUE"
11771172

11781173
def get_io_buffer(self, buffer):
11791174
if isinstance(buffer, io.Buffer):

tests/test_build_res.py

+7-9
Original file line numberDiff line numberDiff line change
@@ -227,16 +227,16 @@ def test_request_clock(self):
227227
clk100_buffer._MustUse__silence = True
228228
clk50_buffer._MustUse__silence = True
229229
self.assertEqual(list(self.cm.iter_clock_constraints()), [
230-
(clk100.i, clk100_port.p, 100e6),
231-
(clk50.i, clk50_port.io, 50e6)
230+
(clk100_port.p, 100e6),
231+
(clk50_port.io, 50e6)
232232
])
233233

234234
def test_add_clock(self):
235235
with _ignore_deprecated():
236236
i2c = self.cm.request("i2c")
237237
self.cm.add_clock_constraint(i2c.scl.o, 100e3)
238238
self.assertEqual(list(self.cm.iter_clock_constraints()), [
239-
(i2c.scl.o, None, 100e3)
239+
(i2c.scl.o, 100e3)
240240
])
241241
((_, _, scl_buffer), (_, _, sda_buffer)) = self.cm.iter_pins()
242242
scl_buffer._MustUse__silence = True
@@ -269,7 +269,7 @@ def test_wrong_lookup(self):
269269

270270
def test_wrong_clock_signal(self):
271271
with self.assertRaisesRegex(TypeError,
272-
r"^Object None is not a Signal$"):
272+
r"^Object None is not a Signal or IOPort$"):
273273
self.cm.add_clock_constraint(None, 10e6)
274274

275275
def test_wrong_clock_frequency(self):
@@ -335,10 +335,8 @@ def test_wrong_request_with_xdr_dict(self):
335335

336336
def test_wrong_clock_constraint_twice(self):
337337
with _ignore_deprecated():
338-
clk100 = self.cm.request("clk100")
339-
(pin, port, buffer), = self.cm.iter_pins()
340-
buffer._MustUse__silence = True
338+
clk100 = self.cm.request("clk100", dir="-")
341339
with self.assertRaisesRegex(ValueError,
342-
(r"^Cannot add clock constraint on \(sig clk100_0__i\), which is already "
340+
(r"^Cannot add clock constraint on \(io-port clk100_0__p\), which is already "
343341
r"constrained to 100000000\.0 Hz$")):
344-
self.cm.add_clock_constraint(clk100.i, 1e6)
342+
self.cm.add_clock_constraint(clk100.p, 1e6)

0 commit comments

Comments
 (0)