4a2605b8d7f54ccfb0531c313a874a18a16b6111
[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 # - verify iowrapper handle correctly multi-bit signals
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_pin, _, _ in ios:
49 for cm_sig, cm_pin, _, _ in cm.get_sig_constraints():
50 if io_pin == cm_pin:
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_pin, _, _ in ios:
60 ios_declaration += "\t" + ios_direction[io_name] + " "
61 if len(io_pin) > 1:
62 ios_declaration += "[{}:0] ".format(len(io_pin - 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_pin, _, _ in ios:
71 wires_declaration += "wire "
72 if len(io_pin) > 1:
73 wires_declaration += "[{}:0] ".format(len(io_pin - 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_pin, io_others, _ in ios:
81 for io_other in io_others:
82 if isinstance(io_other, IOStandard):
83 io_standard = io_other.name
84 trellis_io_declaration += \
85 "(* LOC=\"{}\" *) (* IO_TYPE=\"{}\" *)\n".format(io_pin[0], 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", io_name + "_io", io_name)
90 elif ios_direction[io_name] == "output":
91 trellis_io_declaration += \
92 "TRELLIS_IO #(.DIR(\"OUTPUT\")) {} (.B({}), .I({}));\n".format(
93 io_name + "_buf", io_name + "_io", io_name)
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_pin, _, _ 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")