litex/mibuild/xilinx_ise.py

248 lines
8.1 KiB
Python

import os, struct, subprocess, sys
from decimal import Decimal
from migen.fhdl.std import *
from migen.fhdl.specials import SynthesisDirective
from migen.genlib.cdc import *
from migen.fhdl.structure import _Fragment
from mibuild.generic_platform import *
from mibuild.crg import SimpleCRG
from mibuild import tools
def _add_period_constraint(platform, clk, period):
if period is not None:
platform.add_platform_command("""NET "{clk}" TNM_NET = "GRPclk";
TIMESPEC "TSclk" = PERIOD "GRPclk" """+str(period)+""" ns HIGH 50%;""", clk=clk)
class CRG_SE(SimpleCRG):
def __init__(self, platform, clk_name, rst_name, period=None, rst_invert=False):
SimpleCRG.__init__(self, platform, clk_name, rst_name, rst_invert)
_add_period_constraint(platform, self._clk, period)
class CRG_DS(Module):
def __init__(self, platform, clk_name, rst_name, period=None, rst_invert=False):
reset_less = rst_name is None
self.clock_domains.cd_sys = ClockDomain(reset_less=reset_less)
self._clk = platform.request(clk_name)
_add_period_constraint(platform, self._clk.p, period)
self.specials += Instance("IBUFGDS",
Instance.Input("I", self._clk.p),
Instance.Input("IB", self._clk.n),
Instance.Output("O", self.cd_sys.clk)
)
if not reset_less:
if rst_invert:
self.comb += self.cd_sys.rst.eq(~platform.request(rst_name))
else:
self.comb += self.cd_sys.rst.eq(platform.request(rst_name))
def _format_constraint(c):
if isinstance(c, Pins):
return "LOC=" + c.identifiers[0]
elif isinstance(c, IOStandard):
return "IOSTANDARD=" + c.name
elif isinstance(c, Drive):
return "DRIVE=" + str(c.strength)
elif isinstance(c, Misc):
return c.misc
def _format_ucf(signame, pin, others, resname):
fmt_c = []
for c in [Pins(pin)] + others:
fc = _format_constraint(c)
if fc is not None:
fmt_c.append(fc)
fmt_r = resname[0] + ":" + str(resname[1])
if resname[2] is not None:
fmt_r += "." + resname[2]
return "NET \"" + signame + "\" " + " | ".join(fmt_c) + "; # " + fmt_r + "\n"
def _build_ucf(named_sc, named_pc):
r = ""
for sig, pins, others, resname in named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
r += _format_ucf(sig + "(" + str(i) + ")", p, others, resname)
else:
r += _format_ucf(sig, pins[0], others, resname)
if named_pc:
r += "\n" + "\n\n".join(named_pc)
return r
def _build_xst_files(device, sources, build_name):
prj_contents = ""
for filename, language in sources:
prj_contents += language + " work " + filename + "\n"
tools.write_to_file(build_name + ".prj", prj_contents)
xst_contents = """run
-ifn %s.prj
-top top
-ifmt MIXED
-opt_mode SPEED
-reduce_control_sets auto
-register_balancing yes
-ofn %s.ngc
-p %s""" % (build_name, build_name, device)
tools.write_to_file(build_name + ".xst", xst_contents)
def _run_yosys(device, sources, build_name):
ys_contents = ""
for filename, language in sources:
ys_contents += "read_{} {}\n".format(language, filename)
if device[:2] == "xc":
archcode = device[2:4]
else:
archcode = device[0:2]
arch = {
"6s": "spartan6",
"7a": "artix7",
"7k": "kintex7",
"7v": "virtex7",
"7z": "zynq7000"
}[archcode]
ys_contents += """hierarchy -check -top top
proc; memory; opt; fsm; opt
synth_xilinx -arch {arch} -top top -edif {build_name}.edif""".format(arch=arch, build_name=build_name)
ys_name = build_name + ".ys"
tools.write_to_file(ys_name, ys_contents)
r = subprocess.call(["yosys", ys_name])
if r != 0:
raise OSError("Subprocess failed")
def _is_valid_version(path, v):
try:
Decimal(v)
return os.path.isdir(os.path.join(path, v))
except:
return False
def _run_ise(build_name, ise_path, source, mode, ngdbuild_opt):
if sys.platform == "win32" or sys.platform == "cygwin":
source = False
build_script_contents = "# Autogenerated by mibuild\nset -e\n"
if source:
vers = [ver for ver in os.listdir(ise_path) if _is_valid_version(ise_path, ver)]
tools_version = max(vers)
bits = struct.calcsize("P")*8
xilinx_settings_file = os.path.join(ise_path, tools_version, "ISE_DS", "settings{0}.sh".format(bits))
if not os.path.exists(xilinx_settings_file) and bits == 64:
# if we are on 64-bit system but the toolchain isn't, try the 32-bit env.
xilinx_settings_file = os.path.join(ise_path, tools_version, "ISE_DS", "settings32.sh")
build_script_contents += "source " + xilinx_settings_file + "\n"
if mode == "edif":
build_script_contents += """
ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.edif {build_name}.ngd"""
else:
build_script_contents += """
xst -ifn {build_name}.xst
ngdbuild -uc {build_name}.ucf {build_name}.ngc {build_name}.ngd"""
build_script_contents += """
map -ol high -w -o {build_name}_map.ncd {build_name}.ngd {build_name}.pcf
par -ol high -w {build_name}_map.ncd {build_name}.ncd {build_name}.pcf
bitgen -g LCK_cycle:6 -g Binary:Yes -w {build_name}.ncd {build_name}.bit
"""
build_script_contents = build_script_contents.format(build_name=build_name, ngdbuild_opt=ngdbuild_opt)
build_script_file = "build_" + build_name + ".sh"
tools.write_to_file(build_script_file, build_script_contents)
r = subprocess.call(["bash", build_script_file])
if r != 0:
raise OSError("Subprocess failed")
class XilinxNoRetimingImpl(Module):
def __init__(self, reg):
self.specials += SynthesisDirective("attribute register_balancing of {r} is no", r=reg)
class XilinxNoRetiming:
@staticmethod
def lower(dr):
return XilinxNoRetimingImpl(dr.reg)
class XilinxMultiRegImpl(MultiRegImpl):
def __init__(self, *args, **kwargs):
MultiRegImpl.__init__(self, *args, **kwargs)
self.specials += [SynthesisDirective("attribute shreg_extract of {r} is no", r=r)
for r in self.regs]
class XilinxMultiReg:
@staticmethod
def lower(dr):
return XilinxMultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
class XilinxISEPlatform(GenericPlatform):
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
so = {
NoRetiming: XilinxNoRetiming,
MultiReg: XilinxMultiReg
}
so.update(special_overrides)
return GenericPlatform.get_verilog(self, *args, special_overrides=so, **kwargs)
def get_edif(self, fragment, **kwargs):
return GenericPlatform.get_edif(self, fragment, "UNISIMS", "Xilinx", self.device, **kwargs)
def build(self, fragment, build_dir="build", build_name="top",
ise_path="/opt/Xilinx", source=True, run=True, mode="xst"):
tools.mkdir_noerror(build_dir)
os.chdir(build_dir)
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
self.finalize(fragment)
ngdbuild_opt = ""
if mode == "xst" or mode == "yosys":
v_src, named_sc, named_pc = self.get_verilog(fragment)
v_file = build_name + ".v"
tools.write_to_file(v_file, v_src)
sources = self.sources + [(v_file, "verilog")]
if mode == "xst":
_build_xst_files(self.device, sources, build_name)
isemode = "xst"
else:
_run_yosys(self.device, sources, build_name)
isemode = "edif"
ngdbuild_opt = "-p " + self.device
if mode == "mist":
from mist import synthesize
synthesize(fragment, self.constraint_manager.get_io_signals())
if mode == "edif" or mode == "mist":
e_src, named_sc, named_pc = self.get_edif(fragment)
e_file = build_name + ".edif"
tools.write_to_file(e_file, e_src)
isemode = "edif"
tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc))
if run:
_run_ise(build_name, ise_path, source, isemode, ngdbuild_opt)
os.chdir("..")
def build_arg_ns(self, ns, *args, **kwargs):
for n in ["build_dir", "build_name", "ise_path"]:
attr = getattr(ns, n)
if attr is not None:
kwargs[n] = attr
if ns.no_source:
kwargs["source"] = False
if ns.no_run:
kwargs["run"] = False
self.build(*args, **kwargs)
def add_arguments(self, parser):
parser.add_argument("--build-dir", default=None, help="Set the directory in which to generate files and run ISE")
parser.add_argument("--build-name", default=None, help="Base name for the generated files")
parser.add_argument("--ise-path", default=None, help="ISE installation path (without version directory)")
parser.add_argument("--no-source", action="store_true", help="Do not source ISE settings file")
parser.add_argument("--no-run", action="store_true", help="Only generate files, do not run ISE")