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>
10 from migen
.fhdl
.structure
import _Fragment
12 from litex
.build
.generic_platform
import *
13 from litex
.build
import tools
14 from litex
.build
.lattice
import common
16 # IO Constraints (.lpf) ----------------------------------------------------------------------------
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
)
27 def _format_lpf(signame
, pin
, others
, resname
):
28 fmt_c
= [_format_constraint(c
) for c
in ([Pins(pin
)] + others
)]
30 for pre
, suf
in fmt_c
:
31 lpf
.append(pre
+ "\"" + signame
+ "\"" + suf
+ ";")
35 def _build_lpf(named_sc
, named_pc
, build_name
):
37 lpf
.append("BLOCK RESETPATHS;")
38 lpf
.append("BLOCK ASYNCPATHS;")
39 for sig
, pins
, others
, resname
in named_sc
:
41 for i
, p
in enumerate(pins
):
42 lpf
.append(_format_lpf(sig
+ "[" + str(i
) + "]", p
, others
, resname
))
44 lpf
.append(_format_lpf(sig
, pins
[0], others
, resname
))
46 lpf
.append("\n\n".join(named_pc
))
47 tools
.write_to_file(build_name
+ ".lpf", "\n".join(lpf
))
49 # Yosys/Nextpnr Helpers/Templates ------------------------------------------------------------------
52 "verilog_defaults -push",
53 "verilog_defaults -add -defer",
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}",
60 def _yosys_import_sources(platform
):
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
)
70 def _build_yosys(template
, platform
, nowidelut
, build_name
):
74 build_name
= build_name
,
75 nwl
= "-nowidelut" if nowidelut
else "",
76 read_files
= _yosys_import_sources(platform
)
78 tools
.write_to_file(build_name
+ ".ys", "\n".join(ys
))
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:]
90 elif "285" in package
:
92 elif "381" in package
:
94 elif "554" in package
:
96 elif "756" in package
:
99 raise ValueError("Invalid package {}".format(package
))
100 return (family
, size
, speed_grade
, package
)
102 nextpnr_ecp5_architectures
= {
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",
115 # Script -------------------------------------------------------------------------------------------
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}"
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"):
127 script_contents
= "@echo off\nrem Autogenerated by LiteX / git: " + tools
.get_litex_git_revision() + "\n\n"
128 fail_stmt
= " || exit /b"
131 script_contents
= "# Autogenerated by LiteX / git: " + tools
.get_litex_git_revision() + "\nset -e\n"
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
,
140 speed_grade
= speed_grade
,
141 timefailarg
= "--timing-allow-fail" if not timingstrict
else "",
142 ignoreloops
= "--ignore-loops" if ignoreloops
else "",
144 fail_stmt
= fail_stmt
,
148 script_file
= "build_" + build_name
+ script_ext
149 tools
.write_to_file(script_file
, script_contents
, force_unix
=False)
153 def _run_script(script
):
154 if sys
.platform
in ("win32", "cygwin"):
155 shell
= ["cmd", "/c"]
159 if subprocess
.call(shell
+ [script
]) != 0:
160 raise OSError("Subprocess failed")
162 # LatticeTrellisToolchain --------------------------------------------------------------------------
164 class LatticeTrellisToolchain
:
167 "keep": ("keep", "true"),
171 "mr_false_path": None,
174 "ars_false_path": None,
175 "no_shreg_extract": None
178 special_overrides
= common
.lattice_ecp5_trellis_special_overrides
181 self
.yosys_template
= _yosys_template
182 self
.build_template
= _build_template
183 self
.false_paths
= set() # FIXME: use it
185 def build(self
, platform
, fragment
,
190 timingstrict
= False,
194 spimode
= "fast-read",
197 # Create build directory
198 os
.makedirs(build_dir
, exist_ok
=True)
203 if not isinstance(fragment
, _Fragment
):
204 fragment
= fragment
.get_fragment()
205 platform
.finalize(fragment
)
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
)
214 # Generate design constraints file (.lpf)
215 _build_lpf(named_sc
, named_pc
, build_name
)
217 # Generate Yosys script
218 _build_yosys(self
.yosys_template
, platform
, nowidelut
, build_name
)
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
)]
224 # Generate build script
225 script
= _build_script(False, self
.build_template
, build_name
, architecture
, package
,
226 speed_grade
, timingstrict
, ignoreloops
, bootaddr
, seed
, spimode
)
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
)
239 def add_false_path_constraint(self
, platform
, from_
, to
):
240 from_
.attr
.add("keep")
242 if (to
, from_
) not in self
.false_paths
:
243 self
.false_paths
.add((from_
, to
))
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")
259 def trellis_argdict(args
):
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
,