2013-04-16 16:55:24 -04:00
|
|
|
import os, struct, subprocess, sys
|
2013-02-07 16:07:30 -05:00
|
|
|
from decimal import Decimal
|
|
|
|
|
2013-05-26 12:07:26 -04:00
|
|
|
from migen.fhdl.std import *
|
|
|
|
from migen.fhdl.specials import SynthesisDirective
|
2013-02-23 13:43:12 -05:00
|
|
|
from migen.genlib.cdc import *
|
2013-02-07 16:07:30 -05:00
|
|
|
|
|
|
|
from mibuild.generic_platform import *
|
2013-03-15 13:46:11 -04:00
|
|
|
from mibuild.crg import SimpleCRG
|
2013-02-07 16:07:30 -05:00
|
|
|
from mibuild import tools
|
|
|
|
|
|
|
|
def _add_period_constraint(platform, clk, period):
|
2013-07-04 13:25:29 -04:00
|
|
|
if period is not None:
|
|
|
|
platform.add_platform_command("""NET "{clk}" TNM_NET = "GRPclk";
|
2013-02-07 16:07:30 -05:00
|
|
|
TIMESPEC "TSclk" = PERIOD "GRPclk" """+str(period)+""" ns HIGH 50%;""", clk=clk)
|
|
|
|
|
|
|
|
class CRG_SE(SimpleCRG):
|
2013-07-04 13:25:29 -04:00
|
|
|
def __init__(self, platform, clk_name, rst_name, period=None, rst_invert=False):
|
2013-03-15 06:31:36 -04:00
|
|
|
SimpleCRG.__init__(self, platform, clk_name, rst_name, rst_invert)
|
2013-04-08 14:28:11 -04:00
|
|
|
_add_period_constraint(platform, self._clk, period)
|
2013-02-07 16:07:30 -05:00
|
|
|
|
2013-03-15 13:46:11 -04:00
|
|
|
class CRG_DS(Module):
|
2013-07-04 13:25:29 -04:00
|
|
|
def __init__(self, platform, clk_name, rst_name, period=None, rst_invert=False):
|
2013-05-07 13:09:56 -04:00
|
|
|
reset_less = rst_name is None
|
|
|
|
self.clock_domains.cd_sys = ClockDomain(reset_less=reset_less)
|
2013-02-07 16:07:30 -05:00
|
|
|
self._clk = platform.request(clk_name)
|
|
|
|
_add_period_constraint(platform, self._clk.p, period)
|
2013-03-15 13:46:11 -04:00
|
|
|
self.specials += Instance("IBUFGDS",
|
2013-02-07 16:07:30 -05:00
|
|
|
Instance.Input("I", self._clk.p),
|
|
|
|
Instance.Input("IB", self._clk.n),
|
2013-03-23 14:37:16 -04:00
|
|
|
Instance.Output("O", self.cd_sys.clk)
|
2013-02-07 16:07:30 -05:00
|
|
|
)
|
2013-05-07 13:09:56 -04:00
|
|
|
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))
|
2013-02-07 16:07:30 -05:00
|
|
|
|
|
|
|
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):
|
2013-03-26 14:17:35 -04:00
|
|
|
fmt_c = []
|
|
|
|
for c in [Pins(pin)] + others:
|
|
|
|
fc = _format_constraint(c)
|
|
|
|
if fc is not None:
|
|
|
|
fmt_c.append(fc)
|
2013-02-07 16:07:30 -05:00
|
|
|
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
|
|
|
|
|
2013-02-08 14:31:45 -05:00
|
|
|
def _build_files(device, sources, named_sc, named_pc, build_name):
|
2013-02-07 16:07:30 -05:00
|
|
|
tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc))
|
|
|
|
|
|
|
|
prj_contents = ""
|
2013-02-08 14:25:20 -05:00
|
|
|
for filename, language in sources:
|
|
|
|
prj_contents += language + " work " + filename + "\n"
|
2013-02-07 16:07:30 -05:00
|
|
|
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
|
2013-05-06 08:21:39 -04:00
|
|
|
-register_balancing yes
|
2013-02-07 16:07:30 -05:00
|
|
|
-ofn %s.ngc
|
|
|
|
-p %s""" % (build_name, build_name, device)
|
|
|
|
tools.write_to_file(build_name + ".xst", xst_contents)
|
|
|
|
|
2013-04-16 16:55:24 -04:00
|
|
|
def _is_valid_version(path, v):
|
|
|
|
try:
|
|
|
|
Decimal(v)
|
|
|
|
return os.path.isdir(os.path.join(path, v))
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
|
2013-04-16 16:39:35 -04:00
|
|
|
def _run_ise(build_name, ise_path, source):
|
2013-04-16 16:55:24 -04:00
|
|
|
if sys.platform == "win32" or sys.platform == "cygwin":
|
|
|
|
source = False
|
2013-04-16 16:39:35 -04:00
|
|
|
build_script_contents = "# Autogenerated by mibuild\nset -e\n"
|
|
|
|
if source:
|
2013-04-16 16:55:24 -04:00
|
|
|
vers = [ver for ver in os.listdir(ise_path) if _is_valid_version(ise_path, ver)]
|
2013-04-16 16:39:35 -04:00
|
|
|
tools_version = max(vers)
|
|
|
|
bits = struct.calcsize("P")*8
|
|
|
|
xilinx_settings_file = '%s/%s/ISE_DS/settings%d.sh' % (ise_path, tools_version, bits)
|
|
|
|
build_script_contents += "source " + xilinx_settings_file + "\n"
|
|
|
|
|
|
|
|
build_script_contents += """
|
2013-02-07 16:07:30 -05:00
|
|
|
xst -ifn {build_name}.xst
|
2013-07-04 17:49:12 -04:00
|
|
|
ngdbuild -uc {build_name}.ucf {build_name}.ngc {build_name}.ngd
|
|
|
|
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
|
2013-04-16 16:39:35 -04:00
|
|
|
""".format(build_name=build_name)
|
2013-02-07 16:07:30 -05:00
|
|
|
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")
|
|
|
|
|
2013-04-25 08:57:45 -04:00
|
|
|
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)
|
|
|
|
|
2013-02-23 13:43:12 -05:00
|
|
|
class XilinxMultiRegImpl(MultiRegImpl):
|
2013-04-25 08:57:45 -04:00
|
|
|
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]
|
2013-02-23 13:43:12 -05:00
|
|
|
|
|
|
|
class XilinxMultiReg:
|
|
|
|
@staticmethod
|
|
|
|
def lower(dr):
|
2013-03-15 14:54:25 -04:00
|
|
|
return XilinxMultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
|
2013-02-23 13:43:12 -05:00
|
|
|
|
2013-02-07 16:07:30 -05:00
|
|
|
class XilinxISEPlatform(GenericPlatform):
|
2013-02-23 13:43:12 -05:00
|
|
|
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
|
2013-04-25 08:57:45 -04:00
|
|
|
so = {
|
|
|
|
NoRetiming: XilinxNoRetiming,
|
|
|
|
MultiReg: XilinxMultiReg
|
|
|
|
}
|
2013-02-23 13:43:12 -05:00
|
|
|
so.update(special_overrides)
|
|
|
|
return GenericPlatform.get_verilog(self, *args, special_overrides=so, **kwargs)
|
|
|
|
|
2013-03-15 13:46:11 -04:00
|
|
|
def build(self, fragment, build_dir="build", build_name="top",
|
2013-04-16 16:39:35 -04:00
|
|
|
ise_path="/opt/Xilinx", source=True, run=True):
|
2013-02-07 16:07:30 -05:00
|
|
|
tools.mkdir_noerror(build_dir)
|
|
|
|
os.chdir(build_dir)
|
|
|
|
|
2013-03-15 13:46:11 -04:00
|
|
|
v_src, named_sc, named_pc = self.get_verilog(fragment)
|
2013-02-07 16:07:30 -05:00
|
|
|
v_file = build_name + ".v"
|
|
|
|
tools.write_to_file(v_file, v_src)
|
2013-02-08 14:25:20 -05:00
|
|
|
sources = self.sources + [(v_file, "verilog")]
|
2013-02-08 14:31:45 -05:00
|
|
|
_build_files(self.device, sources, named_sc, named_pc, build_name)
|
|
|
|
if run:
|
2013-04-16 16:39:35 -04:00
|
|
|
_run_ise(build_name, ise_path, source)
|
2013-02-07 16:07:30 -05:00
|
|
|
|
|
|
|
os.chdir("..")
|
2013-02-08 16:23:58 -05:00
|
|
|
|
|
|
|
def build_arg_ns(self, ns, *args, **kwargs):
|
|
|
|
for n in ["build_dir", "build_name", "ise_path"]:
|
2013-06-01 11:22:57 -04:00
|
|
|
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
|
2013-02-08 16:23:58 -05:00
|
|
|
self.build(*args, **kwargs)
|
|
|
|
|
|
|
|
def add_arguments(self, parser):
|
2013-06-01 11:22:57 -04:00
|
|
|
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)")
|
2013-04-16 16:39:35 -04:00
|
|
|
parser.add_argument("--no-source", action="store_true", help="Do not source ISE settings file")
|
2013-02-08 16:23:58 -05:00
|
|
|
parser.add_argument("--no-run", action="store_true", help="Only generate files, do not run ISE")
|