From e9b4543aa5d3d65f1c67f583d5236371dfa33c33 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Feb 2020 23:37:15 +0000 Subject: [PATCH] build.res,vendor: place clock constraint on port, not net, if possible. For most toolchains, these are functionally identical, although ports tend to work a bit better, being the common case. For Vivado, though, it is necessary to place them on the port because its timing analyzer considers input buffer delay. Fixes #301. --- nmigen/build/res.py | 23 ++++++++++++++++++++++- nmigen/vendor/intel.py | 10 +++++++--- nmigen/vendor/lattice_ecp5.py | 19 ++++++++++++------- nmigen/vendor/lattice_ice40.py | 12 ++++++++---- nmigen/vendor/lattice_machxo2.py | 11 ++++++----- nmigen/vendor/xilinx_7series.py | 8 ++++++-- nmigen/vendor/xilinx_spartan_3_6.py | 6 +++--- nmigen/vendor/xilinx_ultrascale.py | 8 ++++++-- 8 files changed, 70 insertions(+), 27 deletions(-) diff --git a/nmigen/build/res.py b/nmigen/build/res.py index afcf41c..5153a5c 100644 --- a/nmigen/build/res.py +++ b/nmigen/build/res.py @@ -226,4 +226,25 @@ class ResourceManager: self._clocks[clock] = float(frequency) def iter_clock_constraints(self): - return iter(self._clocks.items()) + # 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 res, pin, port, attrs in self._ports: + if hasattr(pin, "i"): + if isinstance(res.ios[0], Pins): + pin_i_to_port[pin.i] = port.io + elif isinstance(res.ios[0], DiffPairs): + pin_i_to_port[pin.i] = port.p + else: + assert False + + for net_signal, frequency in self._clocks.items(): + port_signal = pin_i_to_port.get(net_signal) + yield net_signal, port_signal, frequency diff --git a/nmigen/vendor/intel.py b/nmigen/vendor/intel.py index 7ff366d..4b8e4f9 100644 --- a/nmigen/vendor/intel.py +++ b/nmigen/vendor/intel.py @@ -107,15 +107,19 @@ class IntelPlatform(TemplatedPlatform): set_global_assignment -name GENERATE_RBF_FILE ON """, "{{name}}.sdc": r""" - {% for signal, frequency in platform.iter_clock_constraints() -%} - create_clock -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("|")}}] + {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} + {% if port_signal is not none -%} + create_clock -name {{port_signal.name}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + {% else -%} + create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("|")}}] + {% endif %} {% endfor %} """, "{{name}}.srf": r""" {% for warning in platform.quartus_suppressed_warnings %} { "" "" "" "{{name}}.v" { } { } 0 {{warning}} "" 0 0 "Design Software" 0 -1 0 ""} {% endfor %} - """, + """, } command_templates = [ r""" diff --git a/nmigen/vendor/lattice_ecp5.py b/nmigen/vendor/lattice_ecp5.py index 13f04fd..691ac0b 100644 --- a/nmigen/vendor/lattice_ecp5.py +++ b/nmigen/vendor/lattice_ecp5.py @@ -135,8 +135,12 @@ class LatticeECP5Platform(TemplatedPlatform): {%- for key, value in extras.items() %} {{key}}={{value}}{% endfor %}; {% endif %} {% endfor %} - {% for signal, frequency in platform.iter_clock_constraints() -%} - FREQUENCY NET "{{signal|hierarchy(".")}}" {{frequency}} HZ; + {% 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 %} {% endfor %} {{get_override("add_preferences")|default("# (add_preferences placeholder)")}} """ @@ -227,14 +231,15 @@ class LatticeECP5Platform(TemplatedPlatform): IOBUF PORT "{{port_name}}" {%- for key, value in extras.items() %} {{key}}={{value}}{% endfor %}; {% endfor %} - {% for signal, frequency in platform.iter_clock_constraints() -%} - FREQUENCY NET "{{signal|hierarchy("/")}}" {{frequency/1000000}} MHZ; - {% endfor %} {{get_override("add_preferences")|default("# (add_preferences placeholder)")}} """, "{{name}}.sdc": r""" - {% for signal, frequency in platform.iter_clock_constraints() -%} - create_clock -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}] + {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} + {% if port_signal is not none -%} + create_clock -name {{port_signal.name}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + {% else -%} + create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + {% endif %} {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} """, diff --git a/nmigen/vendor/lattice_ice40.py b/nmigen/vendor/lattice_ice40.py index 370f750..e5570ec 100644 --- a/nmigen/vendor/lattice_ice40.py +++ b/nmigen/vendor/lattice_ice40.py @@ -134,8 +134,8 @@ class LatticeICE40Platform(TemplatedPlatform): {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%} set_io {{port_name}} {{pin_name}} {% endfor %} - {% for signal, frequency in platform.iter_clock_constraints() -%} - set_frequency {{signal|hierarchy(".")}} {{frequency/1000000}} + {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} + set_frequency {{net_signal|hierarchy(".")}} {{frequency/1000000}} {% endfor%} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} """, @@ -243,8 +243,12 @@ class LatticeICE40Platform(TemplatedPlatform): """, "{{name}}.sdc": r""" # {{autogenerated}} - {% for signal, frequency in platform.iter_clock_constraints() -%} - create_clock -name {{signal.name}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}] + {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} + {% if port_signal is not none -%} + create_clock -name {{port_signal.name}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + {% else -%} + create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + {% endif %} {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} """, diff --git a/nmigen/vendor/lattice_machxo2.py b/nmigen/vendor/lattice_machxo2.py index 99c093c..962994b 100644 --- a/nmigen/vendor/lattice_machxo2.py +++ b/nmigen/vendor/lattice_machxo2.py @@ -91,14 +91,15 @@ class LatticeMachXO2Platform(TemplatedPlatform): {%- for key, value in extras.items() %} {{key}}={{value}}{% endfor %}; {% endif %} {% endfor %} - {% for signal, frequency in platform.iter_clock_constraints() -%} - FREQUENCY NET "{{signal|hierarchy("/")}}" {{frequency/1000000}} MHZ; - {% endfor %} {{get_override("add_preferences")|default("# (add_preferences placeholder)")}} """, "{{name}}.sdc": r""" - {% for signal, frequency in platform.iter_clock_constraints() -%} - create_clock -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}] + {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} + {% if port_signal is not none -%} + create_clock -name {{port_signal.name}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + {% else -%} + create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + {% endif %} {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} """, diff --git a/nmigen/vendor/xilinx_7series.py b/nmigen/vendor/xilinx_7series.py index 50f6636..99778a3 100644 --- a/nmigen/vendor/xilinx_7series.py +++ b/nmigen/vendor/xilinx_7series.py @@ -133,8 +133,12 @@ class Xilinx7SeriesPlatform(TemplatedPlatform): set_property {{attr_name}} {{attr_value}} [get_ports {{port_name}}] {% endfor %} {% endfor %} - {% for signal, frequency in platform.iter_clock_constraints() -%} - create_clock -name {{signal.name}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}] + {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} + {% if port_signal is not none -%} + create_clock -name {{port_signal.name}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + {% else -%} + create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + {% endif %} {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} """ diff --git a/nmigen/vendor/xilinx_spartan_3_6.py b/nmigen/vendor/xilinx_spartan_3_6.py index a8e265a..6081e41 100644 --- a/nmigen/vendor/xilinx_spartan_3_6.py +++ b/nmigen/vendor/xilinx_spartan_3_6.py @@ -132,9 +132,9 @@ class XilinxSpartan3Or6Platform(TemplatedPlatform): NET "{{port_name}}" {{attr_name}}={{attr_value}}; {% endfor %} {% endfor %} - {% for signal, frequency in platform.iter_clock_constraints() -%} - NET "{{signal|hierarchy("/")}}" TNM_NET="PRD{{signal|hierarchy("/")}}"; - TIMESPEC "TS{{signal|hierarchy("/")}}"=PERIOD "PRD{{signal|hierarchy("/")}}" {{1000000000/frequency}} ns HIGH 50%; + {% 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%; {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} """ diff --git a/nmigen/vendor/xilinx_ultrascale.py b/nmigen/vendor/xilinx_ultrascale.py index 74d5986..31f5f22 100644 --- a/nmigen/vendor/xilinx_ultrascale.py +++ b/nmigen/vendor/xilinx_ultrascale.py @@ -133,8 +133,12 @@ class XilinxUltraScalePlatform(TemplatedPlatform): set_property {{attr_name}} {{attr_value}} [get_ports {{port_name}}] {% endfor %} {% endfor %} - {% for signal, frequency in platform.iter_clock_constraints() -%} - create_clock -name {{signal.name}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}] + {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} + {% if port_signal is not none -%} + create_clock -name {{port_signal.name}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + {% else -%} + create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + {% endif %} {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} """ -- 2.30.2