build/lattice/trellis: optionally allow failure if p&r timing not met
authorGabriel Somlo <gsomlo@gmail.com>
Thu, 24 Oct 2019 17:56:20 +0000 (13:56 -0400)
committerGabriel Somlo <gsomlo@gmail.com>
Thu, 24 Oct 2019 17:56:20 +0000 (13:56 -0400)
When timing requirements are strict, allow the build process to fail upon
failure to meet timing. This facilitates running the build process from a
loop, repeatedly, until a "lucky" p&r solution is found, e.g.:

  while true; do
    litex/boards/targets/versa_ecp5.py --gateware-toolchain trellis \
      --sys-clk-freq=60e06 --cpu-type rocket --cpu-variant linux \
      --with-ethernet --yosys-nowidelut \
      --nextpnr-timingstrict
    if [ "$?" == "0" ]; then
      echo "Success" | mail -s "Build Succeeded" your@email.here
      break
    fi
  done

This augments commit #683e0668, which unconditionally forced p&r to
succeed, regardless of whether timing was met, via '--timing-allow-fail'.

Signed-off-by: Gabriel Somlo <gsomlo@gmail.com>
litex/boards/targets/versa_ecp5.py
litex/build/lattice/trellis.py

index bc9eacb1175325280ffd072d5d006ae1eb710e29..f2d9275f75760ec851c0327cb4277b6048ef915a 100755 (executable)
@@ -11,7 +11,7 @@ from migen.genlib.resetsync import AsyncResetSynchronizer
 
 from litex.boards.platforms import versa_ecp5
 
-from litex.build.lattice.trellis import yosys_args, yosys_argdict
+from litex.build.lattice.trellis import trellis_args, trellis_argdict
 
 from litex.soc.cores.clock import *
 from litex.soc.integration.soc_sdram import *
@@ -135,7 +135,7 @@ def main():
         help='gateware toolchain to use, diamond (default) or  trellis')
     builder_args(parser)
     soc_sdram_args(parser)
-    yosys_args(parser)
+    trellis_args(parser)
     parser.add_argument("--sys-clk-freq", default=75e6,
                         help="system clock frequency (default=75MHz)")
     parser.add_argument("--with-ethernet", action="store_true",
@@ -145,7 +145,7 @@ def main():
     cls = EthernetSoC if args.with_ethernet else BaseSoC
     soc = cls(toolchain=args.toolchain, sys_clk_freq=int(float(args.sys_clk_freq)), **soc_sdram_argdict(args))
     builder = Builder(soc, **builder_argdict(args))
-    builder.build(**yosys_argdict(args))
+    builder.build(**trellis_argdict(args))
 
 if __name__ == "__main__":
     main()
index caf76b170177389f8053ee8dd0d4652b8edc78e7..c194d3f10936a5019e0ebf9ac123004f29490946 100644 (file)
@@ -73,7 +73,7 @@ def _build_lpf(named_sc, named_pc):
 
 
 def _build_script(source, build_template, build_name, architecture,
-                  package, freq_constraint):
+                  package, freq_constraint, timingstrict):
     if sys.platform in ("win32", "cygwin"):
         script_ext = ".bat"
         build_script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
@@ -89,6 +89,7 @@ def _build_script(source, build_template, build_name, architecture,
                                                architecture=architecture,
                                                package=package,
                                                freq_constraint=freq_constraint,
+                                               timefailarg="--timing-allow-fail" if not timingstrict else "",
                                                fail_stmt=fail_stmt)
 
     build_script_file = "build_" + build_name + script_ext
@@ -143,14 +144,16 @@ class LatticeTrellisToolchain:
 
         self.build_template = [
             "yosys -q -l {build_name}.rpt {build_name}.ys",
-            "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --{architecture} --package {package} --freq {freq_constraint} --timing-allow-fail",
+            "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --{architecture} --package {package} --freq {freq_constraint} {timefailarg}",
             "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit"
         ]
 
         self.freq_constraints = dict()
 
     def build(self, platform, fragment, build_dir="build", build_name="top",
-              toolchain_path=None, run=True, nowidelut=False, **kwargs):
+              toolchain_path=None, run=True,
+              nowidelut=False, timingstrict=False,
+              **kwargs):
         if toolchain_path is None:
             toolchain_path = "/usr/share/trellis/"
         os.makedirs(build_dir, exist_ok=True)
@@ -188,7 +191,8 @@ class LatticeTrellisToolchain:
                                   default=0.0))
 
         script = _build_script(False, self.build_template, build_name,
-                               architecture, package, freq_constraint)
+                               architecture, package, freq_constraint,
+                               timingstrict)
 
         # run scripts
         if run:
@@ -204,11 +208,14 @@ class LatticeTrellisToolchain:
     def add_period_constraint(self, platform, clk, period):
         platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
 
-def yosys_args(parser):
+def trellis_args(parser):
     parser.add_argument("--yosys-nowidelut", action="store_true",
                         help="pass '-nowidelut' to yosys synth_ecp5")
+    parser.add_argument("--nextpnr-timingstrict", action="store_true",
+                        help="fail if timing not met, i.e., do NOT pass '--timing-allow-fail' to nextpnr")
 
-def yosys_argdict(args):
+def trellis_argdict(args):
     return {
-        "nowidelut": args.yosys_nowidelut
+        "nowidelut": args.yosys_nowidelut,
+        "timingstrict": args.nextpnr_timingstrict,
     }