build/lattice/prjtrellis: set default toolchain_path to "/opt/prjtrellis"
[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 # - add inout support to iowrapper.
16 # - check/document attr_translate.
17
18
19 nextpnr_ecp5_architectures = {
20 "lfe5u-25f": "25k",
21 "lfe5u-45f": "45k",
22 "lfe5u-85f": "85k",
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",
29 }
30
31
32 def yosys_import_sources(platform):
33 includes = ""
34 reads = []
35 for path in platform.verilog_include_paths:
36 includes += " -I" + path
37 for filename, language, library in platform.sources:
38 reads.append("read_{}{} {}".format(
39 language, includes, filename))
40 return "\n".join(reads)
41
42
43 def generate_prjtrellis_iowrapper(platform, vns):
44 ios, _ = platform.resolve_signals(vns)
45 ios_direction = {}
46 # resolve ios directions
47 cm = platform.constraint_manager
48 for io_name, io_pins, _, _ in ios:
49 for cm_sig, cm_pins, _, _ in cm.get_sig_constraints():
50 if io_pins == cm_pins:
51 ios_direction[io_name] = cm_sig.direction
52 last_io_name = io_name
53
54 iowrapper_contents = []
55 iowrapper_contents.append("module {build_name}_iowrapper(")
56
57 # ios declaration
58 ios_declaration = ""
59 for io_name, io_pins, _, _ in ios:
60 ios_declaration += "\t" + ios_direction[io_name] + " "
61 if len(io_pins) > 1:
62 ios_declaration += "[{}:0] ".format(len(io_pins) - 1)
63 ios_declaration += io_name + "_io"
64 ios_declaration += ",\n" if io_name != last_io_name else ""
65 iowrapper_contents.append(ios_declaration)
66 iowrapper_contents.append(");\n")
67
68 # wires declaration
69 wires_declaration = ""
70 for io_name, io_pins, _, _ in ios:
71 wires_declaration += "wire "
72 if len(io_pins) > 1:
73 wires_declaration += "[{}:0] ".format(len(io_pins) - 1)
74 wires_declaration += io_name
75 wires_declaration += ";\n"
76 iowrapper_contents.append(wires_declaration)
77
78 # trellis_io declaration
79 trellis_io_declaration = ""
80 for io_name, io_pins, io_others, _ in ios:
81 for io_other in io_others:
82 if isinstance(io_other, IOStandard):
83 io_standard = io_other.name
84 for i, io_pin in enumerate(io_pins):
85 trellis_io_declaration += \
86 "(* LOC=\"{}\" *) (* IO_TYPE=\"{}\" *)\n".format(io_pin, io_standard)
87 if ios_direction[io_name] == "input":
88 trellis_io_declaration += \
89 "TRELLIS_IO #(.DIR(\"INPUT\")) {} (.B({}), .O({}));\n".format(
90 io_name + "_buf" + str(i), io_name + "_io[" + str(i) + "]", io_name + "[" + str(i) + "]")
91 elif ios_direction[io_name] == "output":
92 trellis_io_declaration += \
93 "TRELLIS_IO #(.DIR(\"OUTPUT\")) {} (.B({}), .I({}));\n".format(
94 io_name + "_buf" + str(i), io_name + "_io[" + str(i) + "]", io_name + "[" + str(i) + "]")
95 else:
96 raise NotImplementedError
97 iowrapper_contents.append(trellis_io_declaration)
98
99 # top declaration
100 top_declaration = "{build_name} _{build_name}(\n"
101 for io_name, io_pins, _, _ in ios:
102 top_declaration += "\t." + io_name + "(" + io_name + ")"
103 top_declaration += ",\n" if io_name != last_io_name else "\n"
104 top_declaration += ");\n"
105 iowrapper_contents.append(top_declaration)
106
107 iowrapper_contents.append("endmodule")
108
109 iowrapper_contents = "\n".join(iowrapper_contents)
110
111 return iowrapper_contents
112
113
114 class LatticePrjTrellisToolchain:
115 attr_translate = {
116 # FIXME: document
117 "keep": ("keep", "true"),
118 "no_retiming": None,
119 "async_reg": None,
120 "mr_ff": None,
121 "mr_false_path": None,
122 "ars_ff1": None,
123 "ars_ff2": None,
124 "ars_false_path": None,
125 "no_shreg_extract": None
126 }
127
128 special_overrides = common.lattice_ecpx_special_overrides
129
130 def build(self, platform, fragment, build_dir="build", build_name="top",
131 toolchain_path=None, run=True):
132 if toolchain_path is None:
133 toolchain_path = "/opt/prjtrellis/"
134 os.makedirs(build_dir, exist_ok=True)
135 cwd = os.getcwd()
136 os.chdir(build_dir)
137
138 # generate verilog
139 if not isinstance(fragment, _Fragment):
140 fragment = fragment.get_fragment()
141 platform.finalize(fragment)
142
143 v_output = platform.get_verilog(fragment)
144 v_file = build_name + ".v"
145 v_output.write(v_file)
146 platform.add_source(v_file)
147
148 # generate iowrapper (with constraints and trellis_ios)
149 # FIXME: remove when prjtrellis will support constraint files
150 iowrapper_file = build_name + "_iowrapper.v"
151 iowrapper_contents = generate_prjtrellis_iowrapper(platform, v_output.ns)
152 iowrapper_contents = iowrapper_contents.format(build_name=build_name)
153 tools.write_to_file(iowrapper_file, iowrapper_contents)
154 platform.add_source(iowrapper_file)
155
156 # generate yosys script
157 yosys_script_file = build_name + ".ys"
158 yosys_script_contents = [
159 yosys_import_sources(platform),
160 "synth_ecp5 -nomux -json {build_name}.json -top {build_name}_iowrapper"
161 ]
162 yosys_script_contents = "\n".join(yosys_script_contents)
163 yosys_script_contents = yosys_script_contents.format(build_name=build_name)
164 tools.write_to_file(yosys_script_file, yosys_script_contents)
165
166 # transform platform.device to nextpnr's architecture / basecfg
167 (family, size, package) = platform.device.split("-")
168 architecture = nextpnr_ecp5_architectures[(family + "-" + size).lower()]
169 basecfg = "empty_" + (family + "-" + size).lower() + ".config"
170 basecfg = os.path.join(toolchain_path, "misc", "basecfgs", basecfg)
171
172 # generate build script
173 build_script_file = "build_" + build_name + ".sh"
174 build_script_contents = [
175 "yosys -q -l {build_name}.rpt {build_name}.ys",
176 "nextpnr-ecp5 --json {build_name}.json --textcfg {build_name}.config --basecfg {basecfg} --{architecture}",
177 "ecppack {build_name}.config {build_name}.bit"
178
179 ]
180 build_script_contents = "\n".join(build_script_contents)
181 build_script_contents = build_script_contents.format(
182 build_name=build_name,
183 architecture=architecture,
184 basecfg=basecfg)
185 tools.write_to_file(build_script_file, build_script_contents)
186
187 # run scripts
188 if run:
189 if subprocess.call(["bash", build_script_file]) != 0:
190 raise OSError("Subprocess failed")
191
192 os.chdir(cwd)
193
194 return v_output.ns
195
196 def add_period_constraint(self, platform, clk, period):
197 print("TODO: add_period_constraint")