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 # - add inout support to iowrapper.
16 # - check/document attr_translate.
19 nextpnr_ecp5_architectures
= {
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",
32 def yosys_import_sources(platform
):
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
)
43 def generate_prjtrellis_iowrapper(platform
, vns
):
44 ios
, _
= platform
.resolve_signals(vns
)
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
54 iowrapper_contents
= []
55 iowrapper_contents
.append("module {build_name}_iowrapper(")
59 for io_name
, io_pins
, _
, _
in ios
:
60 ios_declaration
+= "\t" + ios_direction
[io_name
] + " "
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")
69 wires_declaration
= ""
70 for io_name
, io_pins
, _
, _
in ios
:
71 wires_declaration
+= "wire "
73 wires_declaration
+= "[{}:0] ".format(len(io_pins
) - 1)
74 wires_declaration
+= io_name
75 wires_declaration
+= ";\n"
76 iowrapper_contents
.append(wires_declaration
)
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
) + "]")
96 raise NotImplementedError
97 iowrapper_contents
.append(trellis_io_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
)
107 iowrapper_contents
.append("endmodule")
109 iowrapper_contents
= "\n".join(iowrapper_contents
)
111 return iowrapper_contents
114 class LatticePrjTrellisToolchain
:
117 "keep": ("keep", "true"),
121 "mr_false_path": None,
124 "ars_false_path": None,
125 "no_shreg_extract": None
128 special_overrides
= common
.lattice_ecpx_special_overrides
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)
139 if not isinstance(fragment
, _Fragment
):
140 fragment
= fragment
.get_fragment()
141 platform
.finalize(fragment
)
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
)
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
)
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"
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
)
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
)
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"
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
,
185 tools
.write_to_file(build_script_file
, build_script_contents
)
189 if subprocess
.call(["bash", build_script_file
]) != 0:
190 raise OSError("Subprocess failed")
196 def add_period_constraint(self
, platform
, clk
, period
):
197 print("TODO: add_period_constraint")