1 # This file is Copyright (c) 2018 Florent Kermarrec <florent@enjoy-digital.fr>
7 from migen
.fhdl
.structure
import _Fragment
9 from litex
.build
.generic_platform
import *
10 from litex
.build
import tools
11 from litex
.build
.lattice
import common
14 # - add timing constraint support.
15 # - check/document attr_translate.
17 nextpnr_ecp5_architectures
= {
21 "lfe5um-25f": "um-25k",
22 "lfe5um-45f": "um-45k",
23 "lfe5um-85f": "um-85k",
24 "lfe5um5g-25f": "um5g-25k",
25 "lfe5um5g-45f": "um5g-45k",
26 "lfe5um5g-85f": "um5g-85k",
30 def _format_constraint(c
):
31 if isinstance(c
, Pins
):
32 return ("LOCATE COMP ", " SITE " + "\"" + c
.identifiers
[0] + "\"")
33 elif isinstance(c
, IOStandard
):
34 return ("IOBUF PORT ", " IO_TYPE=" + c
.name
)
35 elif isinstance(c
, Misc
):
39 def _format_lpf(signame
, pin
, others
, resname
):
40 fmt_c
= [_format_constraint(c
) for c
in ([Pins(pin
)] + others
)]
42 for pre
, suf
in fmt_c
:
43 r
+= pre
+ "\"" + signame
+ "\"" + suf
+ ";\n"
47 def _build_lpf(named_sc
, named_pc
):
48 r
= "BLOCK RESETPATHS;\n"
49 r
+= "BLOCK ASYNCPATHS;\n"
50 for sig
, pins
, others
, resname
in named_sc
:
52 for i
, p
in enumerate(pins
):
53 r
+= _format_lpf(sig
+ "[" + str(i
) + "]", p
, others
, resname
)
55 r
+= _format_lpf(sig
, pins
[0], others
, resname
)
57 r
+= "\n" + "\n\n".join(named_pc
)
61 def yosys_import_sources(platform
):
64 for path
in platform
.verilog_include_paths
:
65 includes
+= " -I" + path
66 for filename
, language
, library
in platform
.sources
:
67 reads
.append("read_{}{} {}".format(
68 language
, includes
, filename
))
69 return "\n".join(reads
)
72 class LatticePrjTrellisToolchain
:
75 "keep": ("keep", "true"),
79 "mr_false_path": None,
82 "ars_false_path": None,
83 "no_shreg_extract": None
86 special_overrides
= common
.lattice_ecpx_prjtrellis_special_overrides
88 def build(self
, platform
, fragment
, build_dir
="build", build_name
="top",
89 toolchain_path
=None, run
=True):
90 if toolchain_path
is None:
91 toolchain_path
= "/usr/share/trellis/"
92 os
.makedirs(build_dir
, exist_ok
=True)
97 if not isinstance(fragment
, _Fragment
):
98 fragment
= fragment
.get_fragment()
99 platform
.finalize(fragment
)
101 top_output
= platform
.get_verilog(fragment
)
102 named_sc
, named_pc
= platform
.resolve_signals(top_output
.ns
)
103 top_file
= build_name
+ ".v"
104 top_output
.write(top_file
)
106 # generate constraints
107 tools
.write_to_file(build_name
+ ".lpf", _build_lpf(named_sc
, named_pc
))
109 # generate yosys script
110 yosys_script_file
= build_name
+ ".ys"
111 yosys_script_contents
= [
112 yosys_import_sources(platform
),
113 "synth_ecp5 -nomux -json {build_name}.json -top {build_name}"
115 yosys_script_contents
= "\n".join(yosys_script_contents
)
116 yosys_script_contents
= yosys_script_contents
.format(build_name
=build_name
)
117 tools
.write_to_file(yosys_script_file
, yosys_script_contents
)
119 # transform platform.device to nextpnr's architecture / basecfg
120 (family
, size
, package
) = platform
.device
.split("-")
121 architecture
= nextpnr_ecp5_architectures
[(family
+ "-" + size
).lower()]
122 basecfg
= "empty_" + (family
+ "-" + size
).lower() + ".config"
123 basecfg
= os
.path
.join(toolchain_path
, "misc", "basecfgs", basecfg
)
125 # generate build script
126 build_script_file
= "build_" + build_name
+ ".sh"
127 build_script_contents
= [
128 "yosys -q -l {build_name}.rpt {build_name}.ys",
129 "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --basecfg {basecfg} --{architecture}",
130 "ecppack {build_name}.config {build_name}.bit"
133 build_script_contents
= "\n".join(build_script_contents
)
134 build_script_contents
= build_script_contents
.format(
135 build_name
=build_name
,
136 architecture
=architecture
,
138 tools
.write_to_file(build_script_file
, build_script_contents
)
142 if subprocess
.call(["bash", build_script_file
]) != 0:
143 raise OSError("Subprocess failed")
149 def add_period_constraint(self
, platform
, clk
, period
):
150 print("TODO: add_period_constraint")