vendor: Add initial support for Symbiflow for Xilinx 7-series
authorMariusz Glebocki <mglebocki@antmicro.com>
Sun, 2 Aug 2020 16:48:26 +0000 (18:48 +0200)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 31 Dec 2021 14:57:41 +0000 (14:57 +0000)
nmigen/vendor/xilinx_7series.py

index e923e03ea54af00b519f2216d05e759a3437faa5..800b5286e0d59113a522a8ca6a9a6678f6d0edb3 100644 (file)
@@ -10,6 +10,9 @@ __all__ = ["Xilinx7SeriesPlatform"]
 
 class Xilinx7SeriesPlatform(TemplatedPlatform):
     """
+    Vivado toolchain
+    ----------------
+
     Required tools:
         * ``vivado``
 
@@ -44,9 +47,26 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
         * ``{{name}}_route.dcp``: Vivado design checkpoint.
         * ``{{name}}.bit``: binary bitstream with metadata.
         * ``{{name}}.bin``: binary bitstream.
+
+    Symbiflow toolchain
+    -------------------
+
+    Required tools:
+        * ``synth``
+        * ``pack``
+        * ``place``
+        * ``route``
+        * ``write_fasm``
+        * ``write_bitstream``
+
+    The environment is populated by running the script specified in the environment variable
+    ``NMIGEN_ENV_Symbiflow``, if present.
+
+    Available overrides:
+        * ``add_constraints``: inserts commands in XDC file.
     """
 
-    toolchain = "Vivado"
+    toolchain = None # selected when creating platform
 
     device  = abstractproperty()
     package = abstractproperty()
@@ -56,8 +76,10 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
     def _part(self):
         return "{}{}-{}".format(self.device, self.package, self.speed)
 
-    required_tools = ["vivado"]
-    file_templates = {
+    # Vivado templates
+
+    _vivado_required_tools = ["vivado"]
+    _vivado_file_templates = {
         **TemplatedPlatform.build_script_templates,
         "build_{{name}}.sh": r"""
             # {{autogenerated}}
@@ -143,7 +165,7 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
             {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
         """
     }
-    command_templates = [
+    _vivado_command_templates = [
         r"""
         {{invoke_tool("vivado")}}
             {{verbose("-verbose")}}
@@ -154,6 +176,127 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
         """
     ]
 
+    # Symbiflow templates
+
+    _symbiflow_part_map = {
+        "xc7a35ticsg324-1L": "xc7a35tcsg324-1", # Arty-A7
+    }
+
+    _symbiflow_required_tools = [
+        "synth",
+        "pack",
+        "place",
+        "route",
+        "write_fasm",
+        "write_bitstream"
+    ]
+    _symbiflow_file_templates = {
+        **TemplatedPlatform.build_script_templates,
+        "{{name}}.v": r"""
+            /* {{autogenerated}} */
+            {{emit_verilog()}}
+        """,
+        "{{name}}.debug.v": r"""
+            /* {{autogenerated}} */
+            {{emit_debug_verilog()}}
+        """,
+        "{{name}}.pcf": r"""
+            # {{autogenerated}}
+            {% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
+                set_io {{port_name}} {{pin_name}}
+            {% endfor %}
+        """,
+        "{{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}} }]
+                {% endfor %}
+            {% endfor %}
+            {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
+        """,
+        "{{name}}.sdc": r"""
+            # {{autogenerated}}
+            {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
+                {% if port_signal is none -%}
+                    create_clock -period {{1000000000/frequency}} {{net_signal.name|ascii_escape}}
+                {% endif %}
+            {% endfor %}
+        """
+    }
+    _symbiflow_command_templates = [
+        r"""
+        {{invoke_tool("synth")}}
+            -t {{name}}
+            -v {% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%} {{file}} {% endfor %} {{name}}.v
+            -p {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
+            -x {{name}}.xdc
+        """,
+        r"""
+        {{invoke_tool("pack")}}
+            -e {{name}}.eblif
+            -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
+            -s {{name}}.sdc
+        """,
+        r"""
+        {{invoke_tool("place")}}
+            -e {{name}}.eblif
+            -p {{name}}.pcf
+            -n {{name}}.net
+            -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
+            -s {{name}}.sdc
+        """,
+        r"""
+        {{invoke_tool("route")}}
+            -e {{name}}.eblif
+            -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
+            -s {{name}}.sdc
+        """,
+        r"""
+        {{invoke_tool("write_fasm")}}
+            -e {{name}}.eblif
+            -P {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
+        """,
+        r"""
+        {{invoke_tool("write_bitstream")}}
+            -f {{name}}.fasm
+            -p {{platform._symbiflow_part_map.get(platform._part, platform._part)}}
+            -b {{name}}.bit
+        """
+    ]
+
+    # Common logic
+
+    def __init__(self, *, toolchain="Vivado"):
+        super().__init__()
+
+        assert toolchain in ("Vivado", "Symbiflow")
+        self.toolchain = toolchain
+
+    @property
+    def required_tools(self):
+        if self.toolchain == "Vivado":
+            return self._vivado_required_tools
+        if self.toolchain == "Symbiflow":
+            return self._symbiflow_required_tools
+        assert False
+
+    @property
+    def file_templates(self):
+        if self.toolchain == "Vivado":
+            return self._vivado_file_templates
+        if self.toolchain == "Symbiflow":
+            return self._symbiflow_file_templates
+        assert False
+
+    @property
+    def command_templates(self):
+        if self.toolchain == "Vivado":
+            return self._vivado_command_templates
+        if self.toolchain == "Symbiflow":
+            return self._symbiflow_command_templates
+        assert False
+
     def create_missing_domain(self, name):
         # Xilinx devices have a global write enable (GWE) signal that asserted during configuraiton
         # and deasserted once it ends. Because it is an asynchronous signal (GWE is driven by logic
@@ -169,17 +312,27 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
                 rst_i = self.request(self.default_rst).i
 
             m = Module()
-            ready = Signal()
-            m.submodules += Instance("STARTUPE2", o_EOS=ready)
-            m.domains += ClockDomain("sync", reset_less=self.default_rst is None)
-            # Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes sim/synth
-            # mismatches with Vivado 2019.2, and the suggested workaround (SIM_DEVICE parameter)
-            # breaks Vivado 2017.4.
-            m.submodules += Instance("BUFGCTRL",
-                i_I0=clk_i,   i_S0=C(1, 1), i_CE0=ready,   i_IGNORE0=C(0, 1),
-                i_I1=C(1, 1), i_S1=C(0, 1), i_CE1=C(0, 1), i_IGNORE1=C(1, 1),
-                o_O=ClockSignal("sync")
-            )
+
+            if self.toolchain == "Vivado":
+                ready = Signal()
+                m.submodules += Instance("STARTUPE2", o_EOS=ready)
+                m.domains += ClockDomain("sync", reset_less=self.default_rst is None)
+                # Actually use BUFGCTRL configured as BUFGCE, since using BUFGCE causes sim/synth
+                # mismatches with Vivado 2019.2, and the suggested workaround (SIM_DEVICE parameter)
+                # breaks Vivado 2017.4.
+                m.submodules += Instance("BUFGCTRL",
+                    i_I0=clk_i,   i_S0=C(1, 1), i_CE0=ready,   i_IGNORE0=C(0, 1),
+                    i_I1=C(1, 1), i_S1=C(0, 1), i_CE1=C(0, 1), i_IGNORE1=C(1, 1),
+                    o_O=ClockSignal("sync")
+                )
+            elif self.toolchain == "Symbiflow":
+                cd_sync = ClockDomain("sync", reset_less=self.default_rst is None)
+                m.domains += cd_sync
+                m.submodules += Instance("BUFG", i_I=clk_i, o_O=cd_sync.clk)
+                self.add_clock_constraint(cd_sync.clk, self.default_clk_frequency)
+            else:
+                assert False
+
             if self.default_rst is not None:
                 m.submodules.reset_sync = ResetSynchronizer(rst_i, domain="sync")
             return m
@@ -307,14 +460,22 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
                             valid_xdrs=(0, 1, 2), valid_attrs=True)
         m = Module()
         i, o, t = self._get_xdr_buffer(m, pin, o_invert=invert)
-        for bit in range(pin.width):
-            m.submodules["{}_{}".format(pin.name, bit)] = Instance("OBUF",
-                i_I=o[bit],
-                o_O=port.io[bit]
-            )
+        if self.toolchain == "Vivado":
+            for bit in range(pin.width):
+                m.submodules["{}_{}".format(pin.name, bit)] = Instance("OBUF",
+                    i_I=o[bit],
+                    o_O=port.io[bit]
+                )
+        elif self.toolchain == "Symbiflow":
+            m.d.comb += port.eq(self._invert_if(invert, o))
+        else:
+            assert False
         return m
 
     def get_tristate(self, pin, port, attrs, invert):
+        if toolchain == "Symbiflow":
+            return super().get_tristate(pin, port, attrs, invert)
+
         self._check_feature("single-ended tristate", pin, attrs,
                             valid_xdrs=(0, 1, 2), valid_attrs=True)
         m = Module()
@@ -328,6 +489,9 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
         return m
 
     def get_input_output(self, pin, port, attrs, invert):
+        if toolchain == "Symbiflow":
+            return super().get_input_output(pin, port, attrs, invert)
+
         self._check_feature("single-ended input/output", pin, attrs,
                             valid_xdrs=(0, 1, 2), valid_attrs=True)
         m = Module()
@@ -342,6 +506,9 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
         return m
 
     def get_diff_input(self, pin, port, attrs, invert):
+        if toolchain == "Symbiflow":
+            return super().get_diff_input(pin, port, attrs, invert)
+
         self._check_feature("differential input", pin, attrs,
                             valid_xdrs=(0, 1, 2), valid_attrs=True)
         m = Module()
@@ -354,6 +521,9 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
         return m
 
     def get_diff_output(self, pin, port, attrs, invert):
+        if toolchain == "Symbiflow":
+            return super().get_diff_output(pin, port, attrs, invert)
+
         self._check_feature("differential output", pin, attrs,
                             valid_xdrs=(0, 1, 2), valid_attrs=True)
         m = Module()
@@ -366,6 +536,9 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
         return m
 
     def get_diff_tristate(self, pin, port, attrs, invert):
+        if toolchain == "Symbiflow":
+            return super().get_diff_tristate(pin, port, attrs, invert)
+
         self._check_feature("differential tristate", pin, attrs,
                             valid_xdrs=(0, 1, 2), valid_attrs=True)
         m = Module()
@@ -379,6 +552,9 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
         return m
 
     def get_diff_input_output(self, pin, port, attrs, invert):
+        if toolchain == "Symbiflow":
+            return super().get_diff_input_output(pin, port, attrs, invert)
+
         self._check_feature("differential input/output", pin, attrs,
                             valid_xdrs=(0, 1, 2), valid_attrs=True)
         m = Module()