1 # This file is Copyright (c) 2018 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
17 # - check/document attr_translate.
19 nextpnr_ecp5_architectures
= {
23 "lfe5um-25f": "um-25k",
24 "lfe5um-45f": "um-45k",
25 "lfe5um-85f": "um-85k",
26 "lfe5um5g-25f": "um5g-25k",
27 "lfe5um5g-45f": "um5g-45k",
28 "lfe5um5g-85f": "um5g-85k",
32 def nextpnr_ecp5_package(package
):
35 elif "381" in package
:
37 elif "554" in package
:
39 elif "756" in package
:
41 raise ValueError("Unknown package")
44 def _format_constraint(c
):
45 if isinstance(c
, Pins
):
46 return ("LOCATE COMP ", " SITE " + "\"" + c
.identifiers
[0] + "\"")
47 elif isinstance(c
, IOStandard
):
48 return ("IOBUF PORT ", " IO_TYPE=" + c
.name
)
49 elif isinstance(c
, Misc
):
50 return ("IOBUF PORT ", " " + c
.misc
)
53 def _format_lpf(signame
, pin
, others
, resname
):
54 fmt_c
= [_format_constraint(c
) for c
in ([Pins(pin
)] + others
)]
56 for pre
, suf
in fmt_c
:
57 r
+= pre
+ "\"" + signame
+ "\"" + suf
+ ";\n"
61 def _build_lpf(named_sc
, named_pc
):
62 r
= "BLOCK RESETPATHS;\n"
63 r
+= "BLOCK ASYNCPATHS;\n"
64 for sig
, pins
, others
, resname
in named_sc
:
66 for i
, p
in enumerate(pins
):
67 r
+= _format_lpf(sig
+ "[" + str(i
) + "]", p
, others
, resname
)
69 r
+= _format_lpf(sig
, pins
[0], others
, resname
)
71 r
+= "\n" + "\n\n".join(named_pc
)
75 def _build_script(source
, build_template
, build_name
, architecture
,
76 package
, freq_constraint
):
77 if sys
.platform
in ("win32", "cygwin"):
79 build_script_contents
= "@echo off\nrem Autogenerated by LiteX / git: " + tools
.get_litex_git_revision() + "\n\n"
80 fail_stmt
= " || exit /b"
83 build_script_contents
= "# Autogenerated by LiteX / git: " + tools
.get_litex_git_revision() + "\nset -e\n"
86 for s
in build_template
:
87 s_fail
= s
+ "{fail_stmt}\n" # Required so Windows scripts fail early.
88 build_script_contents
+= s_fail
.format(build_name
=build_name
,
89 architecture
=architecture
,
91 freq_constraint
=freq_constraint
,
94 build_script_file
= "build_" + build_name
+ script_ext
95 tools
.write_to_file(build_script_file
, build_script_contents
,
97 return build_script_file
100 def _run_script(script
):
101 if sys
.platform
in ("win32", "cygwin"):
102 shell
= ["cmd", "/c"]
106 if subprocess
.call(shell
+ [script
]) != 0:
107 raise OSError("Subprocess failed")
110 def yosys_import_sources(platform
):
113 for path
in platform
.verilog_include_paths
:
114 includes
+= " -I" + path
115 for filename
, language
, library
in platform
.sources
:
116 reads
.append("read_{}{} {}".format(
117 language
, includes
, filename
))
118 return "\n".join(reads
)
121 class LatticeTrellisToolchain
:
124 "keep": ("keep", "true"),
128 "mr_false_path": None,
131 "ars_false_path": None,
132 "no_shreg_extract": None
135 special_overrides
= common
.lattice_ecpx_trellis_special_overrides
138 self
.yosys_template
= [
140 "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0",
141 "synth_ecp5 -abc9 {nwl} -json {build_name}.json -top {build_name}",
144 self
.build_template
= [
145 "yosys -q -l {build_name}.rpt {build_name}.ys",
146 "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --{architecture} --package {package} --freq {freq_constraint} --timing-allow-fail",
147 "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit"
150 self
.freq_constraints
= dict()
152 def build(self
, platform
, fragment
, build_dir
="build", build_name
="top",
153 toolchain_path
=None, run
=True, nowidelut
=False, **kwargs
):
154 if toolchain_path
is None:
155 toolchain_path
= "/usr/share/trellis/"
156 os
.makedirs(build_dir
, exist_ok
=True)
161 if not isinstance(fragment
, _Fragment
):
162 fragment
= fragment
.get_fragment()
163 platform
.finalize(fragment
)
165 top_output
= platform
.get_verilog(fragment
, name
=build_name
, **kwargs
)
166 named_sc
, named_pc
= platform
.resolve_signals(top_output
.ns
)
167 top_file
= build_name
+ ".v"
168 top_output
.write(top_file
)
169 platform
.add_source(top_file
)
171 # generate constraints
172 tools
.write_to_file(build_name
+ ".lpf",
173 _build_lpf(named_sc
, named_pc
))
175 # generate yosys script
176 yosys_script_file
= build_name
+ ".ys"
177 yosys_script_contents
= "\n".join(_
.format(build_name
=build_name
,
178 nwl
="-nowidelut" if nowidelut
else "",
179 read_files
=yosys_import_sources(platform
))
180 for _
in self
.yosys_template
)
181 tools
.write_to_file(yosys_script_file
, yosys_script_contents
)
183 # transform platform.device to nextpnr's architecture
184 (family
, size
, package
) = platform
.device
.split("-")
185 architecture
= nextpnr_ecp5_architectures
[(family
+ "-" + size
).lower()]
186 package
= nextpnr_ecp5_package(package
)
187 freq_constraint
= str(max(self
.freq_constraints
.values(),
190 script
= _build_script(False, self
.build_template
, build_name
,
191 architecture
, package
, freq_constraint
)
201 # Until nextpnr-ecp5 can handle multiple clock domains, use the same
202 # approach as the icestorm and use the fastest clock for timing
204 def add_period_constraint(self
, platform
, clk
, period
):
205 platform
.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(freq
=str(float(1/period
)*1000), clk
="{clk}"), clk
=clk
)
207 def yosys_args(parser
):
208 parser
.add_argument("--yosys-nowidelut", action
="store_true",
209 help="pass '-nowidelut' to yosys synth_ecp5")
211 def yosys_argdict(args
):
213 "nowidelut": args
.yosys_nowidelut