1 # This file is Copyright (c) 2018 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2018 William D. Jones <thor0505@comcast.net>
9 from migen
.fhdl
.structure
import _Fragment
11 from litex
.build
.generic_platform
import *
12 from litex
.build
import tools
13 from litex
.build
.lattice
import common
16 # - check/document attr_translate.
18 nextpnr_ecp5_architectures
= {
22 "lfe5um-25f": "um-25k",
23 "lfe5um-45f": "um-45k",
24 "lfe5um-85f": "um-85k",
25 "lfe5um5g-25f": "um5g-25k",
26 "lfe5um5g-45f": "um5g-45k",
27 "lfe5um5g-85f": "um5g-85k",
31 def nextpnr_ecp5_package(package
):
34 elif "381" in package
:
36 elif "554" in package
:
38 elif "756" in package
:
40 raise ValueError("Unknown package")
43 def _format_constraint(c
):
44 if isinstance(c
, Pins
):
45 return ("LOCATE COMP ", " SITE " + "\"" + c
.identifiers
[0] + "\"")
46 elif isinstance(c
, IOStandard
):
47 return ("IOBUF PORT ", " IO_TYPE=" + c
.name
)
48 elif isinstance(c
, Misc
):
49 return ("IOBUF PORT ", " " + c
.misc
)
52 def _format_lpf(signame
, pin
, others
, resname
):
53 fmt_c
= [_format_constraint(c
) for c
in ([Pins(pin
)] + others
)]
55 for pre
, suf
in fmt_c
:
56 r
+= pre
+ "\"" + signame
+ "\"" + suf
+ ";\n"
60 def _build_lpf(named_sc
, named_pc
):
61 r
= "BLOCK RESETPATHS;\n"
62 r
+= "BLOCK ASYNCPATHS;\n"
63 for sig
, pins
, others
, resname
in named_sc
:
65 for i
, p
in enumerate(pins
):
66 r
+= _format_lpf(sig
+ "[" + str(i
) + "]", p
, others
, resname
)
68 r
+= _format_lpf(sig
, pins
[0], others
, resname
)
70 r
+= "\n" + "\n\n".join(named_pc
)
74 def _build_script(source
, build_template
, build_name
, architecture
,
75 package
, freq_constraint
):
76 if sys
.platform
in ("win32", "cygwin"):
78 build_script_contents
= "@echo off\nrem Autogenerated by Migen\n\n"
79 fail_stmt
= " || exit /b"
82 build_script_contents
= "# Autogenerated by Migen\nset -e\n\n"
85 for s
in build_template
:
86 s_fail
= s
+ "{fail_stmt}\n" # Required so Windows scripts fail early.
87 build_script_contents
+= s_fail
.format(build_name
=build_name
,
88 architecture
=architecture
,
90 freq_constraint
=freq_constraint
,
93 build_script_file
= "build_" + build_name
+ script_ext
94 tools
.write_to_file(build_script_file
, build_script_contents
,
96 return build_script_file
99 def _run_script(script
):
100 if sys
.platform
in ("win32", "cygwin"):
101 shell
= ["cmd", "/c"]
105 if subprocess
.call(shell
+ [script
]) != 0:
106 raise OSError("Subprocess failed")
109 def yosys_import_sources(platform
):
112 for path
in platform
.verilog_include_paths
:
113 includes
+= " -I" + path
114 for filename
, language
, library
in platform
.sources
:
115 reads
.append("read_{}{} {}".format(
116 language
, includes
, filename
))
117 return "\n".join(reads
)
120 class LatticeTrellisToolchain
:
123 "keep": ("keep", "true"),
127 "mr_false_path": None,
130 "ars_false_path": None,
131 "no_shreg_extract": None
134 special_overrides
= common
.lattice_ecpx_trellis_special_overrides
137 self
.yosys_template
= [
139 "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0",
140 "synth_ecp5 -json {build_name}.json -top {build_name}",
143 self
.build_template
= [
144 "yosys -q -l {build_name}.rpt {build_name}.ys",
145 "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --{architecture} --package {package} --freq {freq_constraint}",
146 "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit"
149 self
.freq_constraints
= dict()
151 def build(self
, platform
, fragment
, build_dir
="build", build_name
="top",
152 toolchain_path
=None, run
=True, **kwargs
):
153 if toolchain_path
is None:
154 toolchain_path
= "/usr/share/trellis/"
155 os
.makedirs(build_dir
, exist_ok
=True)
160 if not isinstance(fragment
, _Fragment
):
161 fragment
= fragment
.get_fragment()
162 platform
.finalize(fragment
)
164 top_output
= platform
.get_verilog(fragment
, name
=build_name
, **kwargs
)
165 named_sc
, named_pc
= platform
.resolve_signals(top_output
.ns
)
166 top_file
= build_name
+ ".v"
167 top_output
.write(top_file
)
168 platform
.add_source(top_file
)
170 # generate constraints
171 tools
.write_to_file(build_name
+ ".lpf",
172 _build_lpf(named_sc
, named_pc
))
174 # generate yosys script
175 yosys_script_file
= build_name
+ ".ys"
176 yosys_script_contents
= "\n".join(_
.format(build_name
=build_name
,
177 read_files
=yosys_import_sources(platform
))
178 for _
in self
.yosys_template
)
179 tools
.write_to_file(yosys_script_file
, yosys_script_contents
)
181 # transform platform.device to nextpnr's architecture
182 (family
, size
, package
) = platform
.device
.split("-")
183 architecture
= nextpnr_ecp5_architectures
[(family
+ "-" + size
).lower()]
184 package
= nextpnr_ecp5_package(package
)
185 freq_constraint
= str(max(self
.freq_constraints
.values(),
188 script
= _build_script(False, self
.build_template
, build_name
,
189 architecture
, package
, freq_constraint
)
199 # Until nextpnr-ecp5 can handle multiple clock domains, use the same
200 # approach as the icestorm and use the fastest clock for timing
202 def add_period_constraint(self
, platform
, clk
, period
):
203 platform
.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(freq
=str(float(1/period
)*1000), clk
="{clk}"), clk
=clk
)