From 16ff3bc07f44db67b10124b26b81ebaad585f08f Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Sun, 30 Jan 2022 18:12:15 +0100 Subject: [PATCH] vendor/xilinx: support for yosys nextPNR toolchain --- nmigen/vendor/xilinx.py | 125 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 9 deletions(-) diff --git a/nmigen/vendor/xilinx.py b/nmigen/vendor/xilinx.py index d6d9886..b926aa2 100644 --- a/nmigen/vendor/xilinx.py +++ b/nmigen/vendor/xilinx.py @@ -102,6 +102,20 @@ class XilinxPlatform(TemplatedPlatform): Available overrides: * ``add_constraints``: inserts commands in XDC file. + + .. rubric:: yosys_nextpnr toolchain + + Required tools: + * ``yosys`` + * ``nextpnr-xilinx`` + * ``fasm2frames`` + * ``xc7frames2bit`` + + The environment is populated by running the script specified in the environment variable + ``NMIGEN_ENV_ysosys_nextpnr``, if present. + + Available overrides: + * ``nextpnr_dir``: overrides toolchain db path. """ toolchain = None # selected when creating platform @@ -409,6 +423,86 @@ class XilinxPlatform(TemplatedPlatform): """ ] + # Yosys NextPNR prjxray templates + + _yosys_nextpnr_part_map = { + "xc7a35ticsg324-1L": "xc7a35tcsg324-1", # Arty-A7 35t + "xc7a100ticsg324-1L": "xc7a100tcsg324-1", # Arty-A7 100t + + } + + _yosys_nextpnr_device = { + "xc7a35ti": "xc7a35t", + "xc7a100ti": "xc7a100t", + } + + _yosys_nextpnr_family = { + "xc7a": "artix7", + "xc7z": "zynq7", + } + + _yosys_nextpnr_required_tools = [ + "yosys", + "nextpnr-xilinx", + "fasm2frames", + "xc7frames2bit" + ] + _yosys_nextpnr_file_templates = { + **TemplatedPlatform.build_script_templates, + "{{name}}.v": r""" + /* {{autogenerated}} */ + {{emit_verilog()}} + """, + "{{name}}.debug.v": r""" + /* {{autogenerated}} */ + {{emit_debug_verilog()}} + """, + "{{name}}.xdc": r""" + # {{autogenerated}} + {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%} + {% for attr_name, attr_value in attrs.items() -%} + set_property {{attr_name}} {{attr_value}} [get_ports {{port_name|tcl_escape}}] + set_property LOC {{pin_name}} [get_ports {{port_name|tcl_escape}}] + {% endfor %} + {% endfor %} + """, + } + _yosys_nextpnr_command_templates = [ + r""" + DB_DIR={{get_override("nextpnr_dir")|default("/usr/share/nextpnr")}}/prjxray-db + """, + r""" + CHIPDB_DIR={{get_override("nextpnr_dir")|default("/usr/share/nextpnr")}}/xilinx-chipdb + """, + r""" + PART={{platform._yosys_nextpnr_part_map.get(platform._part, platform._part)}} + """, + r""" + {{invoke_tool("yosys")}} + -p "synth_xilinx -flatten -abc9 -nobram -arch xc7 -top {{name}}; write_json {{name}}.json" {% for file in platform.iter_files(".v", ".sv", ".vhd", ".vhdl") -%} {{file}} {% endfor %} {{name}}.v + """, + r""" + {{invoke_tool("nextpnr-xilinx")}} + --chipdb $CHIPDB_DIR/{{platform._yosys_nextpnr_device.get(platform.device, platform.device)}}.bin + --xdc {{name}}.xdc + --json {{name}}.json + --write {{name}}_routed.json + --fasm {{name}}.fasm + """, + r""" + {{invoke_tool("fasm2frames")}} + --part $PART + --db-root $DB_DIR/{{platform._yosys_nextpnr_family[platform.device[:4]]}} {{name}}.fasm > {{name}}.frames + """, + r""" + {{invoke_tool("xc7frames2bit")}} + --part_file $DB_DIR/{{platform._yosys_nextpnr_family[platform.device[:4]]}}/$PART/part.yaml + --part_name $PART + --frm_file {{name}}.frames + --output_file {{name}}.bit + """, + ] + # Common logic def __init__(self, *, toolchain=None): @@ -482,7 +576,8 @@ class XilinxPlatform(TemplatedPlatform): else: toolchain = "Vivado" - assert toolchain in ("Vivado", "ISE", "Symbiflow") + assert toolchain in ("Vivado", "ISE", "Symbiflow", "yosys_nextpnr") + self.oss_toolchain = False if toolchain == "Vivado": if self.family in ISE_FAMILIES: raise ValueError("Family '{}' is not supported by the Vivado toolchain, please use ISE instead".format(self.family)) @@ -490,8 +585,14 @@ class XilinxPlatform(TemplatedPlatform): if self.family not in ISE_FAMILIES and self.family != "series7": raise ValueError("Family '{}' is not supported by the ISE toolchain, please use Vivado instead".format(self.family)) elif toolchain == "Symbiflow": + self.oss_toolchain = True if self.family != "series7": raise ValueError("Family '{}' is not supported by the Symbiflow toolchain".format(self.family)) + elif toolchain == "yosys_nextpnr": + self.oss_toolchain = True + if self.family != "series7": + raise ValueError("Family '{}' is not supported by the yosys nextpnr toolchain".format(self.family)) + self.toolchain = toolchain @property @@ -502,6 +603,8 @@ class XilinxPlatform(TemplatedPlatform): return self._ise_required_tools if self.toolchain == "Symbiflow": return self._symbiflow_required_tools + if self.toolchain == "yosys_nextpnr": + return self._yosys_nextpnr_required_tools assert False @property @@ -512,6 +615,8 @@ class XilinxPlatform(TemplatedPlatform): return self._ise_file_templates if self.toolchain == "Symbiflow": return self._symbiflow_file_templates + if self.toolchain == "yosys_nextpnr": + return self._yosys_nextpnr_file_templates assert False @property @@ -522,6 +627,8 @@ class XilinxPlatform(TemplatedPlatform): return self._ise_command_templates if self.toolchain == "Symbiflow": return self._symbiflow_command_templates + if self.toolchain == "yosys_nextpnr": + return self._yosys_nextpnr_command_templates assert False def create_missing_domain(self, name): @@ -544,7 +651,7 @@ class XilinxPlatform(TemplatedPlatform): "ultrascaleplus": "STARTUPE3", } - if self.family not in STARTUP_PRIMITIVE or self.toolchain == "Symbiflow": + if self.family not in STARTUP_PRIMITIVE or self.oss_toolchain: # Spartan 3 and before lacks a STARTUP primitive with EOS output; use a simple ResetSynchronizer # in that case, as is the default. # Symbiflow does not support the STARTUPE2 primitive. @@ -888,7 +995,7 @@ class XilinxPlatform(TemplatedPlatform): valid_xdrs=self._get_valid_xdrs(), valid_attrs=True) m = Module() i, o, t = self._get_xdr_buffer(m, pin, attrs.get("IOSTANDARD"), o_invert=invert) - if self.toolchain != "Symbiflow": + if not self.oss_toolchain: for bit in range(pin.width): m.submodules["{}_{}".format(pin.name, bit)] = Instance("OBUF", i_I=o[bit], @@ -899,7 +1006,7 @@ class XilinxPlatform(TemplatedPlatform): return m def get_tristate(self, pin, port, attrs, invert): - if self.toolchain == "Symbiflow": + if self.oss_toolchain: return super().get_tristate(pin, port, attrs, invert) self._check_feature("single-ended tristate", pin, attrs, @@ -915,7 +1022,7 @@ class XilinxPlatform(TemplatedPlatform): return m def get_input_output(self, pin, port, attrs, invert): - if self.toolchain == "Symbiflow": + if self.oss_toolchain: return super().get_input_output(pin, port, attrs, invert) self._check_feature("single-ended input/output", pin, attrs, @@ -932,7 +1039,7 @@ class XilinxPlatform(TemplatedPlatform): return m def get_diff_input(self, pin, port, attrs, invert): - if self.toolchain == "Symbiflow": + if self.oss_toolchain: return super().get_diff_input(pin, port, attrs, invert) self._check_feature("differential input", pin, attrs, @@ -947,7 +1054,7 @@ class XilinxPlatform(TemplatedPlatform): return m def get_diff_output(self, pin, port, attrs, invert): - if self.toolchain == "Symbiflow": + if self.toolchain == ["Symbiflow", "yosys_nextpnr"]: return super().get_diff_output(pin, port, attrs, invert) self._check_feature("differential output", pin, attrs, @@ -962,7 +1069,7 @@ class XilinxPlatform(TemplatedPlatform): return m def get_diff_tristate(self, pin, port, attrs, invert): - if self.toolchain == "Symbiflow": + if self.oss_toolchain: return super().get_diff_tristate(pin, port, attrs, invert) self._check_feature("differential tristate", pin, attrs, @@ -978,7 +1085,7 @@ class XilinxPlatform(TemplatedPlatform): return m def get_diff_input_output(self, pin, port, attrs, invert): - if self.toolchain == "Symbiflow": + if self.oss_toolchain: return super().get_diff_input_output(pin, port, attrs, invert) self._check_feature("differential input/output", pin, attrs, -- 2.30.2