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}",
121 "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit"
124 def _build_script(source
, build_template
, build_name
, architecture
, package
, speed_grade
, timingstrict
, ignoreloops
):
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 "",
143 fail_stmt
= fail_stmt
)
145 script_file
= "build_" + build_name
+ script_ext
146 tools
.write_to_file(script_file
, script_contents
, force_unix
=False)
150 def _run_script(script
):
151 if sys
.platform
in ("win32", "cygwin"):
152 shell
= ["cmd", "/c"]
156 if subprocess
.call(shell
+ [script
]) != 0:
157 raise OSError("Subprocess failed")
159 # LatticeTrellisToolchain --------------------------------------------------------------------------
161 class LatticeTrellisToolchain
:
164 "keep": ("keep", "true"),
168 "mr_false_path": None,
171 "ars_false_path": None,
172 "no_shreg_extract": None
175 special_overrides
= common
.lattice_ecp5_trellis_special_overrides
178 self
.yosys_template
= _yosys_template
179 self
.build_template
= _build_template
180 self
.false_paths
= set() # FIXME: use it
182 def build(self
, platform
, fragment
,
187 timingstrict
= False,
191 # Create build directory
192 os
.makedirs(build_dir
, exist_ok
=True)
197 if not isinstance(fragment
, _Fragment
):
198 fragment
= fragment
.get_fragment()
199 platform
.finalize(fragment
)
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
)
208 # Generate design constraints file (.lpf)
209 _build_lpf(named_sc
, named_pc
, build_name
)
211 # Generate Yosys script
212 _build_yosys(self
.yosys_template
, platform
, nowidelut
, build_name
)
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
)]
218 # Generate build script
219 script
= _build_script(False, self
.build_template
, build_name
, architecture
, package
,
220 speed_grade
, timingstrict
, ignoreloops
)
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
)
234 def add_false_path_constraint(self
, platform
, from_
, to
):
235 from_
.attr
.add("keep")
237 if (to
, from_
) not in self
.false_paths
:
238 self
.false_paths
.add((from_
, to
))
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")
248 def trellis_argdict(args
):
250 "nowidelut": args
.yosys_nowidelut
,
251 "timingstrict": args
.nextpnr_timingstrict
,
252 "ignoreloops": args
.nextpnr_ignoreloops
,