From ca5e46c69ad5ccc51237ae6e6d60398f63652304 Mon Sep 17 00:00:00 2001 From: Mariusz Glebocki Date: Sun, 2 Aug 2020 18:48:26 +0200 Subject: [PATCH] vendor: Add initial support for Symbiflow for Xilinx 7-series --- nmigen/vendor/xilinx_7series.py | 216 +++++++++++++++++++++++++++++--- 1 file changed, 196 insertions(+), 20 deletions(-) diff --git a/nmigen/vendor/xilinx_7series.py b/nmigen/vendor/xilinx_7series.py index e923e03..800b528 100644 --- a/nmigen/vendor/xilinx_7series.py +++ b/nmigen/vendor/xilinx_7series.py @@ -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() -- 2.30.2