vendor/xilinx: support for yosys nextPNR toolchain
authorGwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
Sun, 30 Jan 2022 17:12:15 +0000 (18:12 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 5 Feb 2022 11:40:41 +0000 (11:40 +0000)
nmigen/vendor/xilinx.py

index d6d988608592fa1cf9a40a1a8304e568762b9a69..b926aa271c0a6d808b3162684da317e021996212 100644 (file)
@@ -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,