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 inout support to iowrapper
15 # - check/document attr_translate
18 nextpnr_ecp5_architectures
= {
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",
31 def yosys_import_sources(platform
):
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
)
42 def generate_prjtrellis_iowrapper(platform
, vns
):
43 ios
, _
= platform
.resolve_signals(vns
)
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
53 iowrapper_contents
= []
54 iowrapper_contents
.append("module {build_name}_iowrapper(")
58 for io_name
, io_pins
, _
, _
in ios
:
59 ios_declaration
+= "\t" + ios_direction
[io_name
] + " "
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")
68 wires_declaration
= ""
69 for io_name
, io_pins
, _
, _
in ios
:
70 wires_declaration
+= "wire "
72 wires_declaration
+= "[{}:0] ".format(len(io_pins
) - 1)
73 wires_declaration
+= io_name
74 wires_declaration
+= ";\n"
75 iowrapper_contents
.append(wires_declaration
)
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
) + "]")
95 raise NotImplementedError
96 iowrapper_contents
.append(trellis_io_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
)
106 iowrapper_contents
.append("endmodule")
108 iowrapper_contents
= "\n".join(iowrapper_contents
)
110 return iowrapper_contents
113 class LatticePrjTrellisToolchain
:
116 "keep": ("keep", "true"),
120 "mr_false_path": None,
123 "ars_false_path": None,
124 "no_shreg_extract": None
127 special_overrides
= common
.lattice_ecpx_special_overrides
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)
136 if not isinstance(fragment
, _Fragment
):
137 fragment
= fragment
.get_fragment()
138 platform
.finalize(fragment
)
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
)
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
)
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"
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
)
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
)
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"
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
,
182 tools
.write_to_file(build_script_file
, build_script_contents
)
186 if subprocess
.call(["bash", build_script_file
]) != 0:
187 raise OSError("Subprocess failed")
193 def add_period_constraint(self
, platform
, clk
, period
):
194 print("TODO: add_period_constraint")