build/lattice/trellis: nextpnr now handle LPF timing constraints and multiple clock...
[litex.git] / litex / build / lattice / trellis.py
1 # This file is Copyright (c) 2018-2019 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2018-2019 David Shah <dave@ds0.me>
3 # This file is Copyright (c) 2018 William D. Jones <thor0505@comcast.net>
4 # License: BSD
5
6 import os
7 import subprocess
8 import sys
9
10 from migen.fhdl.structure import _Fragment
11
12 from litex.build.generic_platform import *
13 from litex.build import tools
14 from litex.build.lattice import common
15
16 # IO Constraints (.lpf) ----------------------------------------------------------------------------
17
18 def _format_constraint(c):
19 if isinstance(c, Pins):
20 return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"")
21 elif isinstance(c, IOStandard):
22 return ("IOBUF PORT ", " IO_TYPE=" + c.name)
23 elif isinstance(c, Misc):
24 return ("IOBUF PORT ", " " + c.misc)
25
26
27 def _format_lpf(signame, pin, others, resname):
28 fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)]
29 lpf = []
30 for pre, suf in fmt_c:
31 lpf.append(pre + "\"" + signame + "\"" + suf + ";")
32 return "\n".join(lpf)
33
34
35 def _build_lpf(named_sc, named_pc, build_name):
36 lpf = []
37 lpf.append("BLOCK RESETPATHS;")
38 lpf.append("BLOCK ASYNCPATHS;")
39 for sig, pins, others, resname in named_sc:
40 if len(pins) > 1:
41 for i, p in enumerate(pins):
42 lpf.append(_format_lpf(sig + "[" + str(i) + "]", p, others, resname))
43 else:
44 lpf.append(_format_lpf(sig, pins[0], others, resname))
45 if named_pc:
46 lpf.append("\n\n".join(named_pc))
47 tools.write_to_file(build_name + ".lpf", "\n".join(lpf))
48
49 # Yosys/Nextpnr Helpers/Templates ------------------------------------------------------------------
50
51 _yosys_template = [
52 "{read_files}",
53 "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0",
54 "synth_ecp5 -abc9 {nwl} -json {build_name}.json -top {build_name}",
55 ]
56
57 def _yosys_import_sources(platform):
58 includes = ""
59 reads = []
60 for path in platform.verilog_include_paths:
61 includes += " -I" + path
62 for filename, language, library in platform.sources:
63 reads.append("read_{}{} {}".format(
64 language, includes, filename))
65 return "\n".join(reads)
66
67 def _build_yosys(template, platform, nowidelut, build_name):
68 ys = []
69 for l in template:
70 ys.append(l.format(
71 build_name = build_name,
72 nwl = "-nowidelut" if nowidelut else "",
73 read_files = _yosys_import_sources(platform)
74 ))
75 tools.write_to_file(build_name + ".ys", "\n".join(ys))
76
77 nextpnr_ecp5_architectures = {
78 "lfe5u-25f" : "25k",
79 "lfe5u-45f" : "45k",
80 "lfe5u-85f" : "85k",
81 "lfe5um-25f" : "um-25k",
82 "lfe5um-45f" : "um-45k",
83 "lfe5um-85f" : "um-85k",
84 "lfe5um5g-25f": "um5g-25k",
85 "lfe5um5g-45f": "um5g-45k",
86 "lfe5um5g-85f": "um5g-85k",
87 }
88
89 def nextpnr_ecp5_package(package):
90 if "256" in package:
91 return "CABGA256"
92 elif "285" in package:
93 return "CSFBGA285"
94 elif "381" in package:
95 return "CABGA381"
96 elif "554" in package:
97 return "CABGA554"
98 elif "756" in package:
99 return "CABGA756"
100 raise ValueError("Unknown package {}".format(package))
101
102 # Script -------------------------------------------------------------------------------------------
103
104 _build_template = [
105 "yosys -q -l {build_name}.rpt {build_name}.ys",
106 "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config \
107 --{architecture} --package {package} {timefailarg}",
108 "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit"
109 ]
110
111 def _build_script(source, build_template, build_name, architecture, package, timingstrict):
112 if sys.platform in ("win32", "cygwin"):
113 script_ext = ".bat"
114 script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
115 fail_stmt = " || exit /b"
116 else:
117 script_ext = ".sh"
118 script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
119 fail_stmt = ""
120
121 for s in build_template:
122 s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early.
123 script_contents += s_fail.format(
124 build_name = build_name,
125 architecture = architecture,
126 package = package,
127 timefailarg = "--timing-allow-fail" if not timingstrict else "",
128 fail_stmt = fail_stmt)
129
130 script_file = "build_" + build_name + script_ext
131 tools.write_to_file(script_file, script_contents, force_unix=False)
132
133 return script_file
134
135 def _run_script(script):
136 if sys.platform in ("win32", "cygwin"):
137 shell = ["cmd", "/c"]
138 else:
139 shell = ["bash"]
140
141 if subprocess.call(shell + [script]) != 0:
142 raise OSError("Subprocess failed")
143
144 # LatticeTrellisToolchain --------------------------------------------------------------------------
145
146 class LatticeTrellisToolchain:
147 attr_translate = {
148 # FIXME: document
149 "keep": ("keep", "true"),
150 "no_retiming": None,
151 "async_reg": None,
152 "mr_ff": None,
153 "mr_false_path": None,
154 "ars_ff1": None,
155 "ars_ff2": None,
156 "ars_false_path": None,
157 "no_shreg_extract": None
158 }
159
160 special_overrides = common.lattice_ecpx_trellis_special_overrides
161
162 def __init__(self):
163 self.yosys_template = _yosys_template
164 self.build_template = _build_template
165
166 def build(self, platform, fragment,
167 build_dir = "build",
168 build_name = "top",
169 toolchain_path = None,
170 run = True,
171 nowidelut = False,
172 timingstrict = False,
173 **kwargs):
174
175 # Get default toolchain path (if not specified)
176 if toolchain_path is None:
177 toolchain_path = "/usr/share/trellis/"
178
179 # Create build directory
180 os.makedirs(build_dir, exist_ok=True)
181 cwd = os.getcwd()
182 os.chdir(build_dir)
183
184 # Finalize design
185 if not isinstance(fragment, _Fragment):
186 fragment = fragment.get_fragment()
187 platform.finalize(fragment)
188
189 # Generate verilog
190 v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
191 named_sc, named_pc = platform.resolve_signals(v_output.ns)
192 top_file = build_name + ".v"
193 v_output.write(top_file)
194 platform.add_source(top_file)
195
196 # Generate design constraints file (.lpf)
197 _build_lpf(named_sc, named_pc, build_name)
198
199 # Generate Yosys script
200 _build_yosys(self.yosys_template, platform, nowidelut, build_name)
201
202 # Translate device to Nextpnr architecture/package
203 (family, size, package) = platform.device.split("-")
204 architecture = nextpnr_ecp5_architectures[(family + "-" + size).lower()]
205 package = nextpnr_ecp5_package(package)
206
207 # Generate build script
208 script = _build_script(False, self.build_template, build_name, architecture, package,
209 timingstrict)
210
211 # Run
212 if run:
213 _run_script(script)
214
215 os.chdir(cwd)
216
217 return v_output.ns
218
219 def add_period_constraint(self, platform, clk, period):
220 platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(
221 freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
222
223 def trellis_args(parser):
224 parser.add_argument("--yosys-nowidelut", action="store_true",
225 help="pass '-nowidelut' to yosys synth_ecp5")
226 parser.add_argument("--nextpnr-timingstrict", action="store_true",
227 help="fail if timing not met, i.e., do NOT pass '--timing-allow-fail' to nextpnr")
228
229 def trellis_argdict(args):
230 return {
231 "nowidelut": args.yosys_nowidelut,
232 "timingstrict": args.nextpnr_timingstrict,
233 }