build/lattice/trellis: fix spimode typo.
[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 "verilog_defaults -push",
53 "verilog_defaults -add -defer",
54 "{read_files}",
55 "verilog_defaults -pop",
56 "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0",
57 "synth_ecp5 -abc9 {nwl} -json {build_name}.json -top {build_name}",
58 ]
59
60 def _yosys_import_sources(platform):
61 includes = ""
62 reads = []
63 for path in platform.verilog_include_paths:
64 includes += " -I" + path
65 for filename, language, library in platform.sources:
66 reads.append("read_{}{} {}".format(
67 language, includes, filename))
68 return "\n".join(reads)
69
70 def _build_yosys(template, platform, nowidelut, build_name):
71 ys = []
72 for l in template:
73 ys.append(l.format(
74 build_name = build_name,
75 nwl = "-nowidelut" if nowidelut else "",
76 read_files = _yosys_import_sources(platform)
77 ))
78 tools.write_to_file(build_name + ".ys", "\n".join(ys))
79
80 def nextpnr_ecp5_parse_device(device):
81 device = device.lower()
82 family = device.split("-")[0]
83 size = device.split("-")[1]
84 speed_grade = device.split("-")[2][0]
85 if speed_grade not in ["6", "7", "8"]:
86 raise ValueError("Invalid speed grade {}".format(speed_grade))
87 package = device.split("-")[2][1:]
88 if "256" in package:
89 package = "CABGA256"
90 elif "285" in package:
91 package = "CSFBGA285"
92 elif "381" in package:
93 package = "CABGA381"
94 elif "554" in package:
95 package = "CABGA554"
96 elif "756" in package:
97 package = "CABGA756"
98 else:
99 raise ValueError("Invalid package {}".format(package))
100 return (family, size, speed_grade, package)
101
102 nextpnr_ecp5_architectures = {
103 "lfe5u-12f" : "12k",
104 "lfe5u-25f" : "25k",
105 "lfe5u-45f" : "45k",
106 "lfe5u-85f" : "85k",
107 "lfe5um-25f" : "um-25k",
108 "lfe5um-45f" : "um-45k",
109 "lfe5um-85f" : "um-85k",
110 "lfe5um5g-25f": "um5g-25k",
111 "lfe5um5g-45f": "um5g-45k",
112 "lfe5um5g-85f": "um5g-85k",
113 }
114
115 # Script -------------------------------------------------------------------------------------------
116
117 _build_template = [
118 "yosys -l {build_name}.rpt {build_name}.ys",
119 "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config \
120 --{architecture} --package {package} --speed {speed_grade} {timefailarg} {ignoreloops} --seed {seed}",
121 "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit --bootaddr {bootaddr} --spimode {spimode}"
122 ]
123
124 def _build_script(source, build_template, build_name, architecture, package, speed_grade, timingstrict, ignoreloops, bootaddr, seed, spimode):
125 if sys.platform in ("win32", "cygwin"):
126 script_ext = ".bat"
127 script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
128 fail_stmt = " || exit /b"
129 else:
130 script_ext = ".sh"
131 script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
132 fail_stmt = ""
133
134 for s in build_template:
135 s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early.
136 script_contents += s_fail.format(
137 build_name = build_name,
138 architecture = architecture,
139 package = package,
140 speed_grade = speed_grade,
141 timefailarg = "--timing-allow-fail" if not timingstrict else "",
142 ignoreloops = "--ignore-loops" if ignoreloops else "",
143 bootaddr = bootaddr,
144 fail_stmt = fail_stmt,
145 seed = seed,
146 spimode = spimode)
147
148 script_file = "build_" + build_name + script_ext
149 tools.write_to_file(script_file, script_contents, force_unix=False)
150
151 return script_file
152
153 def _run_script(script):
154 if sys.platform in ("win32", "cygwin"):
155 shell = ["cmd", "/c"]
156 else:
157 shell = ["bash"]
158
159 if subprocess.call(shell + [script]) != 0:
160 raise OSError("Subprocess failed")
161
162 # LatticeTrellisToolchain --------------------------------------------------------------------------
163
164 class LatticeTrellisToolchain:
165 attr_translate = {
166 # FIXME: document
167 "keep": ("keep", "true"),
168 "no_retiming": None,
169 "async_reg": None,
170 "mr_ff": None,
171 "mr_false_path": None,
172 "ars_ff1": None,
173 "ars_ff2": None,
174 "ars_false_path": None,
175 "no_shreg_extract": None
176 }
177
178 special_overrides = common.lattice_ecp5_trellis_special_overrides
179
180 def __init__(self):
181 self.yosys_template = _yosys_template
182 self.build_template = _build_template
183 self.false_paths = set() # FIXME: use it
184
185 def build(self, platform, fragment,
186 build_dir = "build",
187 build_name = "top",
188 run = True,
189 nowidelut = False,
190 timingstrict = False,
191 ignoreloops = False,
192 bootaddr = 0,
193 seed = 1,
194 spimode = "fast-read",
195 **kwargs):
196
197 # Create build directory
198 os.makedirs(build_dir, exist_ok=True)
199 cwd = os.getcwd()
200 os.chdir(build_dir)
201
202 # Finalize design
203 if not isinstance(fragment, _Fragment):
204 fragment = fragment.get_fragment()
205 platform.finalize(fragment)
206
207 # Generate verilog
208 v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
209 named_sc, named_pc = platform.resolve_signals(v_output.ns)
210 top_file = build_name + ".v"
211 v_output.write(top_file)
212 platform.add_source(top_file)
213
214 # Generate design constraints file (.lpf)
215 _build_lpf(named_sc, named_pc, build_name)
216
217 # Generate Yosys script
218 _build_yosys(self.yosys_template, platform, nowidelut, build_name)
219
220 # Translate device to Nextpnr architecture/package/speed_grade
221 (family, size, speed_grade, package) = nextpnr_ecp5_parse_device(platform.device)
222 architecture = nextpnr_ecp5_architectures[(family + "-" + size)]
223
224 # Generate build script
225 script = _build_script(False, self.build_template, build_name, architecture, package,
226 speed_grade, timingstrict, ignoreloops, bootaddr, seed, spimode)
227 # Run
228 if run:
229 _run_script(script)
230
231 os.chdir(cwd)
232
233 return v_output.ns
234
235 def add_period_constraint(self, platform, clk, period):
236 platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(
237 freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
238
239 def add_false_path_constraint(self, platform, from_, to):
240 from_.attr.add("keep")
241 to.attr.add("keep")
242 if (to, from_) not in self.false_paths:
243 self.false_paths.add((from_, to))
244
245 def trellis_args(parser):
246 parser.add_argument("--yosys-nowidelut", action="store_true",
247 help="pass '-nowidelut' to yosys synth_ecp5")
248 parser.add_argument("--nextpnr-timingstrict", action="store_true",
249 help="fail if timing not met, i.e., do NOT pass '--timing-allow-fail' to nextpnr")
250 parser.add_argument("--nextpnr-ignoreloops", action="store_true",
251 help="ignore combinational loops in timing analysis, i.e. pass '--ignore-loops' to nextpnr")
252 parser.add_argument("--ecppack-bootaddr", default=0,
253 help="Set boot address for next image, i.e. pass '--bootaddr xxx' to ecppack")
254 parser.add_argument("--ecppack-spimode", default="fast-read",
255 help="Set slave SPI programming mode")
256 parser.add_argument("--nextpnr-seed", default=1, type=int,
257 help="seed to pass to nextpnr")
258
259 def trellis_argdict(args):
260 return {
261 "nowidelut": args.yosys_nowidelut,
262 "timingstrict": args.nextpnr_timingstrict,
263 "ignoreloops": args.nextpnr_ignoreloops,
264 "bootaddr": args.ecppack_bootaddr,
265 "spimode": args.ecppack_spimode,
266 "seed": args.nextpnr_seed,
267 }