build/xilinx/vivado: improve directive support
[litex.git] / litex / build / xilinx / ise.py
1 # This file is Copyright (c) 2014-2019 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2014-2015 Robert Jordens <jordens@gmail.com>
3 # This file is Copyright (c) 2014-2015 Sebastien Bourdeauducq <sb@m-labs.hk>
4 # This file is Copyright (c) 2017 bunnie <bunnie@kosagi.com>
5 # This file is Copyright (c) 2018-2017 Tim 'mithro' Ansell <me@mith.ro>
6 # This file is Copyright (c) 2018 William D. Jones <thor0505@comcast.net>
7 # This file is Copyright (c) 2019 Larry Doolittle <ldoolitt@recycle.lbl.gov>
8
9 # License: BSD
10
11
12 import os
13 import subprocess
14 import sys
15
16 from migen.fhdl.structure import _Fragment
17
18 from litex.build.generic_platform import *
19 from litex.build import tools
20 from litex.build.xilinx import common
21
22
23 def _format_constraint(c):
24 if isinstance(c, Pins):
25 return "LOC=" + c.identifiers[0]
26 elif isinstance(c, IOStandard):
27 return "IOSTANDARD=" + c.name
28 elif isinstance(c, Drive):
29 return "DRIVE=" + str(c.strength)
30 elif isinstance(c, Misc):
31 return c.misc
32
33
34 def _format_ucf(signame, pin, others, resname):
35 fmt_c = []
36 for c in [Pins(pin)] + others:
37 fc = _format_constraint(c)
38 if fc is not None:
39 fmt_c.append(fc)
40 fmt_r = resname[0] + ":" + str(resname[1])
41 if resname[2] is not None:
42 fmt_r += "." + resname[2]
43 return "NET \"" + signame + "\" " + " | ".join(fmt_c) + "; # " + fmt_r + "\n"
44
45
46 def _build_ucf(named_sc, named_pc):
47 r = ""
48 for sig, pins, others, resname in named_sc:
49 if len(pins) > 1:
50 for i, p in enumerate(pins):
51 r += _format_ucf(sig + "(" + str(i) + ")", p, others, resname)
52 else:
53 r += _format_ucf(sig, pins[0], others, resname)
54 if named_pc:
55 r += "\n" + "\n\n".join(named_pc)
56 return r
57
58
59 def _build_xst_files(device, sources, vincpaths, build_name, xst_opt):
60 prj_contents = ""
61 for filename, language, library in sources:
62 prj_contents += language + " " + library + " " + tools.cygpath(filename) + "\n"
63 tools.write_to_file(build_name + ".prj", prj_contents)
64
65 xst_contents = """run
66 -ifn {build_name}.prj
67 -top {build_name}
68 {xst_opt}
69 -ofn {build_name}.ngc
70 -p {device}
71 """.format(build_name=build_name, xst_opt=xst_opt, device=device)
72 if vincpaths:
73 xst_contents += "-vlgincdir {"
74 for path in vincpaths:
75 xst_contents += tools.cygpath(path) + " "
76 xst_contents += "}"
77 tools.write_to_file(build_name + ".xst", xst_contents)
78
79
80 def _run_yosys(device, sources, vincpaths, build_name):
81 ys_contents = ""
82 incflags = ""
83 for path in vincpaths:
84 incflags += " -I" + path
85 for filename, language, library in sources:
86 ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
87
88 ys_contents += """hierarchy -check -top top
89 proc; memory; opt; fsm; opt
90 synth_xilinx -top top -edif {build_name}.edif""".format(build_name=build_name)
91
92 ys_name = build_name + ".ys"
93 tools.write_to_file(ys_name, ys_contents)
94 r = subprocess.call(["yosys", ys_name])
95 if r != 0:
96 raise OSError("Subprocess failed")
97
98
99 def _run_ise(build_name, ise_path, source, mode, ngdbuild_opt,
100 toolchain, platform, ver=None):
101 if sys.platform == "win32" or sys.platform == "cygwin":
102 source_cmd = "call "
103 script_ext = ".bat"
104 shell = ["cmd", "/c"]
105 build_script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n"
106 fail_stmt = " || exit /b"
107 else:
108 source_cmd = "source "
109 script_ext = ".sh"
110 shell = ["bash"]
111 build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
112 fail_stmt = ""
113 if source:
114 settings = common.settings(ise_path, ver, "ISE_DS")
115 build_script_contents += source_cmd + tools.cygpath(settings) + "\n"
116 if mode == "edif":
117 ext = "edif"
118 else:
119 ext = "ngc"
120 build_script_contents += """
121 xst -ifn {build_name}.xst{fail_stmt}
122 """
123
124 # This generates a .v file for post synthesis simulation
125 build_script_contents += """
126 netgen -ofmt verilog -w -sim {build_name}.{ext} {build_name}_synth.v
127 """
128
129 build_script_contents += """
130 ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd{fail_stmt}
131 """
132 if mode == "cpld":
133 build_script_contents += """
134 cpldfit -ofmt verilog {par_opt} -p {device} {build_name}.ngd{fail_stmt}
135 taengine -f {build_name}.vm6 -detail -iopath -l {build_name}.tim{fail_stmt}
136 hprep6 -s IEEE1532 -i {build_name}.vm6{fail_stmt}
137 """
138 else:
139 build_script_contents += """
140 map {map_opt} -o {build_name}_map.ncd {build_name}.ngd {build_name}.pcf{fail_stmt}
141 par {par_opt} {build_name}_map.ncd {build_name}.ncd {build_name}.pcf{fail_stmt}
142 bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit{fail_stmt}
143 """
144 build_script_contents = build_script_contents.format(build_name=build_name,
145 ngdbuild_opt=ngdbuild_opt, bitgen_opt=toolchain.bitgen_opt, ext=ext,
146 par_opt=toolchain.par_opt, map_opt=toolchain.map_opt,
147 device=platform.device, fail_stmt=fail_stmt)
148 build_script_contents += toolchain.ise_commands.format(build_name=build_name)
149 build_script_file = "build_" + build_name + script_ext
150 tools.write_to_file(build_script_file, build_script_contents, force_unix=False)
151 command = shell + [build_script_file]
152 r = tools.subprocess_call_filtered(command, common.colors)
153 if r != 0:
154 raise OSError("Subprocess failed")
155
156
157 class XilinxISEToolchain:
158 attr_translate = {
159 "keep": ("keep", "true"),
160 "no_retiming": ("register_balancing", "no"),
161 "async_reg": None,
162 "mr_ff": None,
163 "ars_ff1": None,
164 "ars_ff2": None,
165 "no_shreg_extract": ("shreg_extract", "no")
166 }
167
168 def __init__(self):
169 self.xst_opt = """-ifmt MIXED
170 -use_new_parser yes
171 -opt_mode SPEED
172 -register_balancing yes"""
173 self.map_opt = "-ol high -w"
174 self.par_opt = "-ol high -w"
175 self.ngdbuild_opt = ""
176 self.bitgen_opt = "-g Binary:Yes -w"
177 self.ise_commands = ""
178
179 def build(self, platform, fragment, build_dir="build", build_name="top",
180 toolchain_path=None, source=True, run=True, mode="xst", **kwargs):
181 if not isinstance(fragment, _Fragment):
182 fragment = fragment.get_fragment()
183 if toolchain_path is None:
184 if sys.platform == "win32":
185 toolchain_path = "C:\\Xilinx"
186 elif sys.platform == "cygwin":
187 toolchain_path = "/cygdrive/c/Xilinx"
188 else:
189 toolchain_path = "/opt/Xilinx"
190
191 platform.finalize(fragment)
192 ngdbuild_opt = self.ngdbuild_opt
193 vns = None
194
195 os.makedirs(build_dir, exist_ok=True)
196 cwd = os.getcwd()
197 os.chdir(build_dir)
198 try:
199 if mode in ("xst", "yosys", "cpld"):
200 v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
201 vns = v_output.ns
202 named_sc, named_pc = platform.resolve_signals(vns)
203 v_file = build_name + ".v"
204 v_output.write(v_file)
205 sources = platform.sources | {(v_file, "verilog", "work")}
206 if mode in ("xst", "cpld"):
207 _build_xst_files(platform.device, sources, platform.verilog_include_paths, build_name, self.xst_opt)
208 isemode = mode
209 else:
210 _run_yosys(platform.device, sources, platform.verilog_include_paths, build_name)
211 isemode = "edif"
212 ngdbuild_opt += "-p " + platform.device
213
214 if mode == "mist":
215 from mist import synthesize
216 synthesize(fragment, platform.constraint_manager.get_io_signals())
217
218 if mode == "edif" or mode == "mist":
219 e_output = platform.get_edif(fragment)
220 vns = e_output.ns
221 named_sc, named_pc = platform.resolve_signals(vns)
222 e_file = build_name + ".edif"
223 e_output.write(e_file)
224 isemode = "edif"
225
226 tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc))
227 if run:
228 _run_ise(build_name, toolchain_path, source, isemode,
229 ngdbuild_opt, self, platform)
230 finally:
231 os.chdir(cwd)
232
233 return vns
234
235 # ISE is broken and you must use *separate* TNM_NET objects for period
236 # constraints and other constraints otherwise it will be unable to trace
237 # them through clock objects like DCM and PLL objects.
238
239 def add_period_constraint(self, platform, clk, period):
240 platform.add_platform_command(
241 """
242 NET "{clk}" TNM_NET = "PRD{clk}";
243 TIMESPEC "TS{clk}" = PERIOD "PRD{clk}" """ + str(period) + """ ns HIGH 50%;
244 """,
245 clk=clk,
246 )
247
248 def add_false_path_constraint(self, platform, from_, to):
249 platform.add_platform_command(
250 """
251 NET "{from_}" TNM_NET = "TIG{from_}";
252 NET "{to}" TNM_NET = "TIG{to}";
253 TIMESPEC "TS{from_}TO{to}" = FROM "TIG{from_}" TO "TIG{to}" TIG;
254 """,
255 from_=from_,
256 to=to,
257 )