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