e27ae1c6a1c3cce4232a05719415ac43e2a50a72
[litex.git] / litex / build / lattice / prjtrellis.py
1 # This file is Copyright (c) 2018 Florent Kermarrec <florent@enjoy-digital.fr>
2 # License: BSD
3
4 import os
5 import subprocess
6
7 from migen.fhdl.structure import _Fragment
8
9 from litex.build.generic_platform import *
10 from litex.build import tools
11 from litex.build.lattice import common
12
13 # TODO:
14 # - add timing constraint support.
15 # - check/document attr_translate.
16
17 nextpnr_ecp5_architectures = {
18 "lfe5u-25f": "25k",
19 "lfe5u-45f": "45k",
20 "lfe5u-85f": "85k",
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",
27 }
28
29
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):
36 return c.misc
37
38
39 def _format_lpf(signame, pin, others, resname):
40 fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)]
41 r = ""
42 for pre, suf in fmt_c:
43 r += pre + "\"" + signame + "\"" + suf + ";\n"
44 return r
45
46
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:
51 if len(pins) > 1:
52 for i, p in enumerate(pins):
53 r += _format_lpf(sig + "[" + str(i) + "]", p, others, resname)
54 else:
55 r += _format_lpf(sig, pins[0], others, resname)
56 if named_pc:
57 r += "\n" + "\n\n".join(named_pc)
58 return r
59
60
61 def yosys_import_sources(platform):
62 includes = ""
63 reads = []
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)
70
71
72 class LatticePrjTrellisToolchain:
73 attr_translate = {
74 # FIXME: document
75 "keep": ("keep", "true"),
76 "no_retiming": None,
77 "async_reg": None,
78 "mr_ff": None,
79 "mr_false_path": None,
80 "ars_ff1": None,
81 "ars_ff2": None,
82 "ars_false_path": None,
83 "no_shreg_extract": None
84 }
85
86 special_overrides = common.lattice_ecpx_prjtrellis_special_overrides
87
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)
93 cwd = os.getcwd()
94 os.chdir(build_dir)
95
96 # generate verilog
97 if not isinstance(fragment, _Fragment):
98 fragment = fragment.get_fragment()
99 platform.finalize(fragment)
100
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)
105
106 # generate constraints
107 tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc))
108
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}"
114 ]
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)
118
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)
124
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"
131
132 ]
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,
137 basecfg=basecfg)
138 tools.write_to_file(build_script_file, build_script_contents)
139
140 # run scripts
141 if run:
142 if subprocess.call(["bash", build_script_file]) != 0:
143 raise OSError("Subprocess failed")
144
145 os.chdir(cwd)
146
147 return top_output.ns
148
149 def add_period_constraint(self, platform, clk, period):
150 print("TODO: add_period_constraint")