Merge pull request #473 from fjullien/memusage
[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}",
121 "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit"
122 ]
123
124 def _build_script(source, build_template, build_name, architecture, package, speed_grade, timingstrict, ignoreloops):
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 fail_stmt = fail_stmt)
144
145 script_file = "build_" + build_name + script_ext
146 tools.write_to_file(script_file, script_contents, force_unix=False)
147
148 return script_file
149
150 def _run_script(script):
151 if sys.platform in ("win32", "cygwin"):
152 shell = ["cmd", "/c"]
153 else:
154 shell = ["bash"]
155
156 if subprocess.call(shell + [script]) != 0:
157 raise OSError("Subprocess failed")
158
159 # LatticeTrellisToolchain --------------------------------------------------------------------------
160
161 class LatticeTrellisToolchain:
162 attr_translate = {
163 # FIXME: document
164 "keep": ("keep", "true"),
165 "no_retiming": None,
166 "async_reg": None,
167 "mr_ff": None,
168 "mr_false_path": None,
169 "ars_ff1": None,
170 "ars_ff2": None,
171 "ars_false_path": None,
172 "no_shreg_extract": None
173 }
174
175 special_overrides = common.lattice_ecp5_trellis_special_overrides
176
177 def __init__(self):
178 self.yosys_template = _yosys_template
179 self.build_template = _build_template
180 self.false_paths = set() # FIXME: use it
181
182 def build(self, platform, fragment,
183 build_dir = "build",
184 build_name = "top",
185 run = True,
186 nowidelut = False,
187 timingstrict = False,
188 ignoreloops = False,
189 **kwargs):
190
191 # Create build directory
192 os.makedirs(build_dir, exist_ok=True)
193 cwd = os.getcwd()
194 os.chdir(build_dir)
195
196 # Finalize design
197 if not isinstance(fragment, _Fragment):
198 fragment = fragment.get_fragment()
199 platform.finalize(fragment)
200
201 # Generate verilog
202 v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
203 named_sc, named_pc = platform.resolve_signals(v_output.ns)
204 top_file = build_name + ".v"
205 v_output.write(top_file)
206 platform.add_source(top_file)
207
208 # Generate design constraints file (.lpf)
209 _build_lpf(named_sc, named_pc, build_name)
210
211 # Generate Yosys script
212 _build_yosys(self.yosys_template, platform, nowidelut, build_name)
213
214 # Translate device to Nextpnr architecture/package/speed_grade
215 (family, size, speed_grade, package) = nextpnr_ecp5_parse_device(platform.device)
216 architecture = nextpnr_ecp5_architectures[(family + "-" + size)]
217
218 # Generate build script
219 script = _build_script(False, self.build_template, build_name, architecture, package,
220 speed_grade, timingstrict, ignoreloops)
221
222 # Run
223 if run:
224 _run_script(script)
225
226 os.chdir(cwd)
227
228 return v_output.ns
229
230 def add_period_constraint(self, platform, clk, period):
231 platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(
232 freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
233
234 def add_false_path_constraint(self, platform, from_, to):
235 from_.attr.add("keep")
236 to.attr.add("keep")
237 if (to, from_) not in self.false_paths:
238 self.false_paths.add((from_, to))
239
240 def trellis_args(parser):
241 parser.add_argument("--yosys-nowidelut", action="store_true",
242 help="pass '-nowidelut' to yosys synth_ecp5")
243 parser.add_argument("--nextpnr-timingstrict", action="store_true",
244 help="fail if timing not met, i.e., do NOT pass '--timing-allow-fail' to nextpnr")
245 parser.add_argument("--nextpnr-ignoreloops", action="store_true",
246 help="ignore combinational loops in timing analysis, i.e. pass '--ignore-loops' to nextpnr")
247
248 def trellis_argdict(args):
249 return {
250 "nowidelut": args.yosys_nowidelut,
251 "timingstrict": args.nextpnr_timingstrict,
252 "ignoreloops": args.nextpnr_ignoreloops,
253 }