vendor: `yosys` is not a required tool for proprietary toolchains.
[nmigen.git] / nmigen / vendor / lattice_ecp5.py
index 8852fb82eab61d8a69f6440ef9e352c5375496fe..c1a1366148903e3ac3d0deaf8bbf691681cecfc7 100644 (file)
@@ -71,15 +71,13 @@ class LatticeECP5Platform(TemplatedPlatform):
     # Trellis templates
 
     _nextpnr_device_options = {
-        "LFE5U-12F":    "--25k",
+        "LFE5U-12F":    "--12k",
         "LFE5U-25F":    "--25k",
         "LFE5U-45F":    "--45k",
         "LFE5U-85F":    "--85k",
-        "LFE5UM-12F":   "--um-25k",
         "LFE5UM-25F":   "--um-25k",
         "LFE5UM-45F":   "--um-45k",
         "LFE5UM-85F":   "--um-85k",
-        "LFE5UM5G-12F": "--um5g-25k",
         "LFE5UM5G-25F": "--um5g-25k",
         "LFE5UM5G-45F": "--um5g-45k",
         "LFE5UM5G-85F": "--um5g-85k",
@@ -103,13 +101,20 @@ class LatticeECP5Platform(TemplatedPlatform):
             # {{autogenerated}}
             {{emit_rtlil()}}
         """,
+        "{{name}}.debug.v": r"""
+            /* {{autogenerated}} */
+            {{emit_debug_verilog()}}
+        """,
         "{{name}}.ys": r"""
             # {{autogenerated}}
             {% for file in platform.iter_extra_files(".v") -%}
-                read_verilog {{get_override("read_opts")|options}} {{file}}
+                read_verilog {{get_override("read_verilog_opts")|options}} {{file}}
             {% endfor %}
             {% for file in platform.iter_extra_files(".sv") -%}
-                read_verilog -sv {{get_override("read_opts")|options}} {{file}}
+                read_verilog -sv {{get_override("read_verilog_opts")|options}} {{file}}
+            {% endfor %}
+            {% for file in platform.iter_extra_files(".il") -%}
+                read_ilang {{file}}
             {% endfor %}
             read_ilang {{name}}.il
             {{get_override("script_after_read")|default("# (script_after_read placeholder)")}}
@@ -121,27 +126,33 @@ 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 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 signal, frequency in platform.iter_clock_constraints() -%}
-                FREQUENCY NET "{{signal|hierarchy(".")}}" {{frequency}} HZ;
+            {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
+                {% if port_signal is not none -%}
+                    FREQUENCY PORT "{{port_signal.name}}" {{frequency}} HZ;
+                {% else -%}
+                    FREQUENCY NET "{{net_signal|hierarchy(".")}}" {{frequency}} HZ;
+                {% endif %}
             {% endfor %}
             {{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
         """
     }
     _trellis_command_templates = [
         r"""
-        {{get_tool("yosys")}}
+        {{invoke_tool("yosys")}}
             {{quiet("-q")}}
             {{get_override("yosys_opts")|options}}
             -l {{name}}.rpt
             {{name}}.ys
         """,
         r"""
-        {{get_tool("nextpnr-ecp5")}}
+        {{invoke_tool("nextpnr-ecp5")}}
             {{quiet("--quiet")}}
             {{get_override("nextpnr_opts")|options}}
             --log {{name}}.tim
@@ -153,7 +164,7 @@ class LatticeECP5Platform(TemplatedPlatform):
             --textcfg {{name}}.config
         """,
         r"""
-        {{get_tool("ecppack")}}
+        {{invoke_tool("ecppack")}}
             {{verbose("--verbose")}}
             {{get_override("ecppack_opts")|options}}
             --input {{name}}.config
@@ -194,7 +205,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}}
@@ -205,7 +216,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"""
@@ -217,14 +228,15 @@ class LatticeECP5Platform(TemplatedPlatform):
                 IOBUF PORT "{{port_name}}"
                     {%- for key, value in extras.items() %} {{key}}={{value}}{% endfor %};
             {% endfor %}
-            {% for signal, frequency in platform.iter_clock_constraints() -%}
-                FREQUENCY NET "{{signal|hierarchy("/")}}" {{frequency/1000000}} MHZ;
-            {% endfor %}
             {{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
         """,
         "{{name}}.sdc": r"""
-            {% for signal, frequency in platform.iter_clock_constraints() -%}
-                create_clock -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}]
+            {% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
+                {% if port_signal is not none -%}
+                    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|tcl_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_escape}}]
+                {% endif %}
             {% endfor %}
             {{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
         """,
@@ -232,16 +244,16 @@ class LatticeECP5Platform(TemplatedPlatform):
     _diamond_command_templates = [
         # These don't have any usable command-line option overrides.
         r"""
-        {{get_tool("pnmainc")}}
+        {{invoke_tool("pnmainc")}}
             {{name}}.tcl
         """,
         r"""
-        {{get_tool("ddtcmd")}}
+        {{invoke_tool("ddtcmd")}}
             -oft -bit
             -if {{name}}_impl/{{name}}_impl.bit -of {{name}}.bit
         """,
         r"""
-        {{get_tool("ddtcmd")}}
+        {{invoke_tool("ddtcmd")}}
             -oft -svfsingle -revd -op "Fast Program"
             -if {{name}}_impl/{{name}}_impl.bit -of {{name}}.svf
         """,
@@ -279,15 +291,33 @@ class LatticeECP5Platform(TemplatedPlatform):
             return self._diamond_command_templates
         assert False
 
+    @property
+    def default_clk_constraint(self):
+        if self.default_clk == "OSCG":
+            return Clock(310e6 / self.oscg_div)
+        return super().default_clk_constraint
+
     def create_missing_domain(self, name):
-        # Lattice ECP devices have two global set/reset signals: PUR, which is driven at startup
+        # Lattice ECP5 devices have two global set/reset signals: PUR, which is driven at startup
         # by the configuration logic and unconditionally resets every storage element, and GSR,
         # which is driven by user logic and each storage element may be configured as affected or
         # unaffected by GSR. PUR is purely asynchronous, so even though it is a low-skew global
         # network, its deassertion may violate a setup/hold constraint with relation to a user
         # clock. To avoid this, a GSR/SGSR instance should be driven synchronized to user clock.
         if name == "sync" and self.default_clk is not None:
-            clk_i = self.request(self.default_clk).i
+            m = Module()
+            if self.default_clk == "OSCG":
+                if not hasattr(self, "oscg_div"):
+                    raise ValueError("OSCG divider (oscg_div) must be an integer between 2 "
+                                     "and 128")
+                if not isinstance(self.oscg_div, int) or self.oscg_div < 2 or self.oscg_div > 128:
+                    raise ValueError("OSCG divider (oscg_div) must be an integer between 2 "
+                                     "and 128, not {!r}"
+                                     .format(self.oscg_div))
+                clk_i = Signal()
+                m.submodules += Instance("OSCG", p_DIV=self.oscg_div, o_OSC=clk_i)
+            else:
+                clk_i = self.request(self.default_clk).i
             if self.default_rst is not None:
                 rst_i = self.request(self.default_rst).i
             else:
@@ -295,7 +325,6 @@ class LatticeECP5Platform(TemplatedPlatform):
 
             gsr0 = Signal()
             gsr1 = Signal()
-            m = Module()
             # There is no end-of-startup signal on ECP5, but PUR is released after IOB enable, so
             # a simple reset synchronizer (with PUR as the asynchronous reset) does the job.
             m.submodules += [
@@ -538,5 +567,5 @@ class LatticeECP5Platform(TemplatedPlatform):
             )
         return m
 
-    # CDC primitives are not currently specialized for ECP5. While Diamond supports the necessary
-    # attributes (TBD); nextpnr-ecp5 does not.
+    # CDC primitives are not currently specialized for ECP5.
+    # While Diamond supports false path constraints; nextpnr-ecp5 does not.