From 805772eea6291d158441ad0e808f8cbfe023cfcb Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 2 May 2020 10:41:18 +0000 Subject: [PATCH] plat, vendor: systematically escape net and file names in Tcl. Before this commit, there was only occasional quoting of some names used in any Tcl files. (I'm not sure what I was thinking.) After this commit, any substs that may include Tcl special characters are escaped. This does not include build names (which are explicitly restricted to ASCII to avoid this problem), or attribute names (which are chosen from a predefined set). Ideally we'd use a more principled approach but Jinja2 does not support custom escaping mechanisms. Note that Vivado restricts clock names to a more restrictive set that forbids using Tcl special characters even when escaped. Fixes #375. --- nmigen/build/plat.py | 13 +++++++++++++ nmigen/vendor/intel.py | 18 +++++++++--------- nmigen/vendor/lattice_ecp5.py | 14 +++++++------- nmigen/vendor/lattice_ice40.py | 6 +++--- nmigen/vendor/lattice_machxo2.py | 12 ++++++------ nmigen/vendor/xilinx_7series.py | 12 ++++++------ nmigen/vendor/xilinx_ultrascale.py | 12 ++++++------ 7 files changed, 50 insertions(+), 37 deletions(-) diff --git a/nmigen/build/plat.py b/nmigen/build/plat.py index d9f2ab2..17f147b 100644 --- a/nmigen/build/plat.py +++ b/nmigen/build/plat.py @@ -368,6 +368,17 @@ class TemplatedPlatform(Platform): def hierarchy(signal, separator): return separator.join(name_map[signal][1:]) + def ascii_escape(string): + def escape_one(match): + if match.group(1) is None: + return match.group(2) + else: + return "_{:02x}_".format(ord(match.group(1)[0])) + return "".join(escape_one(m) for m in re.finditer(r"([^A-Za-z0-9_])|(.)", string)) + + def tcl_escape(string): + return "{" + re.sub(r"([{}\\])", r"\\\1", string) + "}" + def verbose(arg): if "NMIGEN_verbose" in os.environ: return arg @@ -387,6 +398,8 @@ class TemplatedPlatform(Platform): trim_blocks=True, lstrip_blocks=True, undefined=jinja2.StrictUndefined) compiled.environment.filters["options"] = options compiled.environment.filters["hierarchy"] = hierarchy + compiled.environment.filters["ascii_escape"] = ascii_escape + compiled.environment.filters["tcl_escape"] = tcl_escape except jinja2.TemplateSyntaxError as e: e.args = ("{} (at {}:{})".format(e.message, origin, e.lineno),) raise diff --git a/nmigen/vendor/intel.py b/nmigen/vendor/intel.py index 2f0c81f..07ee95b 100644 --- a/nmigen/vendor/intel.py +++ b/nmigen/vendor/intel.py @@ -85,22 +85,22 @@ class IntelPlatform(TemplatedPlatform): {% endif %} {% for file in platform.iter_extra_files(".v") -%} - set_global_assignment -name VERILOG_FILE "{{file}}" + set_global_assignment -name VERILOG_FILE {{file|tcl_escape}} {% endfor %} {% for file in platform.iter_extra_files(".sv") -%} - set_global_assignment -name SYSTEMVERILOG_FILE "{{file}}" + set_global_assignment -name SYSTEMVERILOG_FILE {{file|tcl_escape}} {% endfor %} {% for file in platform.iter_extra_files(".vhd", ".vhdl") -%} - set_global_assignment -name VHDL_FILE "{{file}}" + set_global_assignment -name VHDL_FILE {{file|tcl_escape}} {% endfor %} set_global_assignment -name VERILOG_FILE {{name}}.v set_global_assignment -name TOP_LEVEL_ENTITY {{name}} set_global_assignment -name DEVICE {{platform.device}}{{platform.package}}{{platform.speed}}{{platform.suffix}} - {% for port_name, pin_name, extras in platform.iter_port_constraints_bits() -%} - set_location_assignment -to "{{port_name}}" PIN_{{pin_name}} - {% for key, value in extras.items() -%} - set_instance_assignment -to "{{port_name}}" -name {{key}} "{{value}}" + {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%} + set_location_assignment -to {{port_name|tcl_escape}} PIN_{{pin_name}} + {% for key, value in attrs.items() -%} + set_instance_assignment -to {{port_name|tcl_escape}} -name {{key}} {{value|tcl_escape}} {% endfor %} {% endfor %} @@ -109,9 +109,9 @@ class IntelPlatform(TemplatedPlatform): "{{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}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + create_clock -name {{port_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}] {% else -%} - create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("|")}}] + create_clock -name {{net_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("|")|tcl_escape}}] {% endif %} {% endfor %} """, diff --git a/nmigen/vendor/lattice_ecp5.py b/nmigen/vendor/lattice_ecp5.py index 53e1626..cfa20ee 100644 --- a/nmigen/vendor/lattice_ecp5.py +++ b/nmigen/vendor/lattice_ecp5.py @@ -126,11 +126,11 @@ class LatticeECP5Platform(TemplatedPlatform): # {{autogenerated}} BLOCK ASYNCPATHS; BLOCK RESETPATHS; - {% for port_name, pin_name, extras in platform.iter_port_constraints_bits() -%} + {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%} LOCATE COMP "{{port_name}}" SITE "{{pin_name}}"; - {% if extras -%} + {% if attrs -%} IOBUF PORT "{{port_name}}" - {%- for key, value in extras.items() %} {{key}}={{value}}{% endfor %}; + {%- for key, value in attrs.items() %} {{key}}={{value}}{% endfor %}; {% endif %} {% endfor %} {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%} @@ -206,7 +206,7 @@ class LatticeECP5Platform(TemplatedPlatform): -lpf {{name}}.lpf \ -synthesis synplify {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%} - prj_src add "{{file}}" + prj_src add {{file|tcl_escape}} {% endfor %} prj_src add {{name}}.v prj_impl option top {{name}} @@ -217,7 +217,7 @@ class LatticeECP5Platform(TemplatedPlatform): prj_run Translate -impl impl -forceAll prj_run Map -impl impl -forceAll prj_run PAR -impl impl -forceAll - prj_run Export -impl "impl" -forceAll -task Bitgen + prj_run Export -impl impl -forceAll -task Bitgen {{get_override("script_after_export")|default("# (script_after_export placeholder)")}} """, "{{name}}.lpf": r""" @@ -234,9 +234,9 @@ class LatticeECP5Platform(TemplatedPlatform): "{{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}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + create_clock -name {{port_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}] {% else -%} - create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + create_clock -name {{net_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}] {% 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 94dd02a..4617d03 100644 --- a/nmigen/vendor/lattice_ice40.py +++ b/nmigen/vendor/lattice_ice40.py @@ -224,7 +224,7 @@ class LatticeICE40Platform(TemplatedPlatform): "{{name}}_syn.prj": r""" # {{autogenerated}} {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%} - add_file -verilog {{file}} + add_file -verilog {{file|tcl_escape}} {% endfor %} add_file -verilog {{name}}.v add_file -constraint {{name}}.sdc @@ -246,9 +246,9 @@ class LatticeICE40Platform(TemplatedPlatform): # {{autogenerated}} {% 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}}] + create_clock -name {{port_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}] {% else -%} - create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + create_clock -name {{net_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}] {% 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 962994b..3ce9962 100644 --- a/nmigen/vendor/lattice_machxo2.py +++ b/nmigen/vendor/lattice_machxo2.py @@ -66,7 +66,7 @@ class LatticeMachXO2Platform(TemplatedPlatform): -lpf {{name}}.lpf \ -synthesis synplify {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%} - prj_src add "{{file}}" + prj_src add {{file|tcl_escape}} {% endfor %} prj_src add {{name}}.v prj_impl option top {{name}} @@ -84,11 +84,11 @@ class LatticeMachXO2Platform(TemplatedPlatform): # {{autogenerated}} BLOCK ASYNCPATHS; BLOCK RESETPATHS; - {% for port_name, pin_name, extras in platform.iter_port_constraints_bits() -%} + {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%} LOCATE COMP "{{port_name}}" SITE "{{pin_name}}"; - {% if extras -%} + {% if attrs -%} IOBUF PORT "{{port_name}}" - {%- for key, value in extras.items() %} {{key}}={{value}}{% endfor %}; + {%- for key, value in attrs.items() %} {{key}}={{value}}{% endfor %}; {% endif %} {% endfor %} {{get_override("add_preferences")|default("# (add_preferences placeholder)")}} @@ -96,9 +96,9 @@ class LatticeMachXO2Platform(TemplatedPlatform): "{{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}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + create_clock -name {{port_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}] {% else -%} - create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + create_clock -name {{net_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}] {% 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 128035d..5da57a6 100644 --- a/nmigen/vendor/xilinx_7series.py +++ b/nmigen/vendor/xilinx_7series.py @@ -78,12 +78,12 @@ class Xilinx7SeriesPlatform(TemplatedPlatform): # {{autogenerated}} create_project -force -name {{name}} -part {{platform.device}}{{platform.package}}-{{platform.speed}}{{"-" + platform.grade if platform.grade else ""}} {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%} - add_files {{file}} + add_files {{file|tcl_escape}} {% endfor %} add_files {{name}}.v read_xdc {{name}}.xdc {% for file in platform.iter_extra_files(".xdc") -%} - read_xdc {{file}} + read_xdc {{file|tcl_escape}} {% endfor %} {{get_override("script_after_read")|default("# (script_after_read placeholder)")}} synth_design -top {{name}} @@ -128,16 +128,16 @@ class Xilinx7SeriesPlatform(TemplatedPlatform): "{{name}}.xdc": r""" # {{autogenerated}} {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%} - set_property LOC {{pin_name}} [get_ports {{port_name}}] + set_property LOC {{pin_name}} [get_ports {{port_name|tcl_escape}}] {% for attr_name, attr_value in attrs.items() -%} - set_property {{attr_name}} {{attr_value}} [get_ports {{port_name}}] + set_property {{attr_name}} {{attr_value|tcl_escape}} [get_ports {{port_name|tcl_escape}}] {% 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}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}] {% else -%} - create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}] {% endif %} {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} diff --git a/nmigen/vendor/xilinx_ultrascale.py b/nmigen/vendor/xilinx_ultrascale.py index 113753c..d4136fb 100644 --- a/nmigen/vendor/xilinx_ultrascale.py +++ b/nmigen/vendor/xilinx_ultrascale.py @@ -78,12 +78,12 @@ class XilinxUltraScalePlatform(TemplatedPlatform): # {{autogenerated}} create_project -force -name {{name}} -part {{platform.device}}-{{platform.package}}-{{platform.speed}}{{"-" + platform.grade if platform.grade else ""}} {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%} - add_files {{file}} + add_files {{file|tcl_escape}} {% endfor %} add_files {{name}}.v read_xdc {{name}}.xdc {% for file in platform.iter_extra_files(".xdc") -%} - read_xdc {{file}} + read_xdc {{file|tcl_escape}} {% endfor %} {{get_override("script_after_read")|default("# (script_after_read placeholder)")}} synth_design -top {{name}} @@ -128,16 +128,16 @@ class XilinxUltraScalePlatform(TemplatedPlatform): "{{name}}.xdc": r""" # {{autogenerated}} {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%} - set_property LOC {{pin_name}} [get_ports {{port_name}}] + set_property LOC {{pin_name}} [get_ports {{port_name|tcl_escape}}] {% for attr_name, attr_value in attrs.items() -%} - set_property {{attr_name}} {{attr_value}} [get_ports {{port_name}}] + set_property {{attr_name}} {{attr_value|tcl_escape}} [get_ports {{port_name|tcl_escape}}] {% 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}} -period {{1000000000/frequency}} [get_ports {{port_signal.name}}] + create_clock -name {{port_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}] {% else -%} - create_clock -name {{net_signal.name}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")}}] + create_clock -name {{net_signal.name|ascii_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}] {% endif %} {% endfor %} {{get_override("add_constraints")|default("# (add_constraints placeholder)")}} -- 2.30.2