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 ------------------------------------------------------------------
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}",
57 def _yosys_import_sources(platform
):
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
)
67 def _build_yosys(template
, platform
, nowidelut
, build_name
):
71 build_name
= build_name
,
72 nwl
= "-nowidelut" if nowidelut
else "",
73 read_files
= _yosys_import_sources(platform
)
75 tools
.write_to_file(build_name
+ ".ys", "\n".join(ys
))
77 nextpnr_ecp5_architectures
= {
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",
89 def nextpnr_ecp5_package(package
):
92 elif "285" in package
:
94 elif "381" in package
:
96 elif "554" in package
:
98 elif "756" in package
:
100 raise ValueError("Unknown package {}".format(package
))
102 # Script -------------------------------------------------------------------------------------------
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"
111 def _build_script(source
, build_template
, build_name
, architecture
, package
, timingstrict
):
112 if sys
.platform
in ("win32", "cygwin"):
114 script_contents
= "@echo off\nrem Autogenerated by LiteX / git: " + tools
.get_litex_git_revision() + "\n\n"
115 fail_stmt
= " || exit /b"
118 script_contents
= "# Autogenerated by LiteX / git: " + tools
.get_litex_git_revision() + "\nset -e\n"
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
,
127 timefailarg
= "--timing-allow-fail" if not timingstrict
else "",
128 fail_stmt
= fail_stmt
)
130 script_file
= "build_" + build_name
+ script_ext
131 tools
.write_to_file(script_file
, script_contents
, force_unix
=False)
135 def _run_script(script
):
136 if sys
.platform
in ("win32", "cygwin"):
137 shell
= ["cmd", "/c"]
141 if subprocess
.call(shell
+ [script
]) != 0:
142 raise OSError("Subprocess failed")
144 # LatticeTrellisToolchain --------------------------------------------------------------------------
146 class LatticeTrellisToolchain
:
149 "keep": ("keep", "true"),
153 "mr_false_path": None,
156 "ars_false_path": None,
157 "no_shreg_extract": None
160 special_overrides
= common
.lattice_ecpx_trellis_special_overrides
163 self
.yosys_template
= _yosys_template
164 self
.build_template
= _build_template
166 def build(self
, platform
, fragment
,
169 toolchain_path
= None,
172 timingstrict
= False,
175 # Get default toolchain path (if not specified)
176 if toolchain_path
is None:
177 toolchain_path
= "/usr/share/trellis/"
179 # Create build directory
180 os
.makedirs(build_dir
, exist_ok
=True)
185 if not isinstance(fragment
, _Fragment
):
186 fragment
= fragment
.get_fragment()
187 platform
.finalize(fragment
)
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
)
196 # Generate design constraints file (.lpf)
197 _build_lpf(named_sc
, named_pc
, build_name
)
199 # Generate Yosys script
200 _build_yosys(self
.yosys_template
, platform
, nowidelut
, build_name
)
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
)
207 # Generate build script
208 script
= _build_script(False, self
.build_template
, build_name
, architecture
, package
,
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
)
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")
229 def trellis_argdict(args
):
231 "nowidelut": args
.yosys_nowidelut
,
232 "timingstrict": args
.nextpnr_timingstrict
,