commit fb5130fc1fbebe88d68f04e96b6b2819b180eea5 Author: Sebastien Bourdeauducq Date: Thu Feb 7 22:07:30 2013 +0100 Initial version diff --git a/mibuild/__init__.py b/mibuild/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mibuild/crg.py b/mibuild/crg.py new file mode 100644 index 000000000..0d4dcfbb5 --- /dev/null +++ b/mibuild/crg.py @@ -0,0 +1,18 @@ +from migen.fhdl.structure import * + +class CRG: + def get_clock_domains(self): + r = dict() + for k, v in self.__dict__.items(): + if isinstance(v, ClockDomain): + r[v.name] = v + return r + + def get_fragment(self): + return Fragment() + +class SimpleCRG(CRG): + def __init__(self, platform, clk_name, rst_name): + self.cd = ClockDomain("sys") + platform.request(clk_name, None, self.cd.clk) + platform.request(rst_name, None, self.cd.rst) diff --git a/mibuild/generic_platform.py b/mibuild/generic_platform.py new file mode 100644 index 000000000..387755288 --- /dev/null +++ b/mibuild/generic_platform.py @@ -0,0 +1,209 @@ +from copy import copy + +from migen.fhdl.structure import * +from migen.corelogic.record import Record +from migen.fhdl import verilog + +class ConstraintError(Exception): + pass + +class Pins: + def __init__(self, *identifiers): + self.identifiers = identifiers + +class IOStandard: + def __init__(self, name): + self.name = name + +class Drive: + def __init__(self, strength): + self.strength = strength + +class Misc: + def __init__(self, misc): + self.misc = misc + +class Subsignal: + def __init__(self, name, *constraints): + self.name = name + self.constraints = list(constraints) + +def _lookup(description, name, number): + for resource in description: + if resource[0] == name and (number is None or resource[1] == number): + return resource + return ConstraintError("Resource not found") + +def _resource_type(resource): + t = None + for element in resource[2:]: + if isinstance(element, Pins): + assert(t is None) + t = len(element.identifiers) + elif isinstance(element, Subsignal): + if t is None: + t = [] + assert(isinstance(t, list)) + n_bits = None + for c in element.constraints: + if isinstance(c, Pins): + assert(n_bits is None) + n_bits = len(c.identifiers) + t.append((element.name, n_bits)) + return t + +def _match(description, requests): + available = list(description) + matched = [] + + # 1. Match requests for a specific number + for request in requests: + if request[1] is not None: + resource = _lookup(available, request[0], request[1]) + available.remove(resource) + matched.append((resource, request[2])) + + # 2. Match requests for no specific number + for request in requests: + if request[1] is None: + resource = _lookup(available, request[0], request[1]) + available.remove(resource) + matched.append((resource, request[2])) + + return matched + +def _separate_pins(constraints): + pins = None + others = [] + for c in constraints: + if isinstance(c, Pins): + assert(pins is None) + pins = c.identifiers + else: + others.append(c) + return pins, others + +class ConstraintManager: + def __init__(self, description): + self.description = description + self.requests = [] + self.platform_commands = [] + + def request(self, name, number=None, obj=None): + r = _lookup(self.description, name, number) + t = _resource_type(r) + + # If obj is None, then create it. + # If it already exists, do some sanity checking. + if obj is None: + if isinstance(t, int): + obj = Signal(t, name_override=r[0]) + else: + obj = Record(t) + else: + if isinstance(t, int): + assert(isinstance(obj, Signal) and obj.nbits == t) + else: + for e in t: + sig = getattr(obj, e[0]) + assert(isinstance(sig, Signal) and sig.nbits == e[1]) + + # Register the request + self.requests.append((name, number, obj)) + + return obj + + def add_platform_command(self, command, **signals): + self.platform_commands.append((command, signals)) + + def get_io_signals(self): + s = set() + for req in self.requests: + obj = req[2] + if isinstance(obj, Signal): + s.add(obj) + else: + for k in obj.__dict__: + p = getattr(obj, k) + if isinstance(p, Signal): + s.add(p) + return s + + def get_sig_constraints(self): + r = [] + matched = _match(self.description, self.requests) + for resource, obj in matched: + name = resource[0] + number = resource[1] + has_subsignals = False + top_constraints = [] + for element in resource[2:]: + if isinstance(element, Subsignal): + has_subsignals = True + else: + top_constraints.append(element) + if has_subsignals: + for element in resource[2:]: + if isinstance(element, Subsignal): + sig = getattr(obj, element.name) + pins, others = _separate_pins(top_constraints + element.constraints) + r.append((sig, pins, others, (name, number, element.name))) + else: + pins, others = _separate_pins(top_constraints) + r.append((obj, pins, others, (name, number, None))) + return r + + def get_platform_commands(self): + return self.platform_commands + + def save(self): + return copy(self.requests), copy(self.platform_commands) + + def restore(self, backup): + self.request, self.platform_commands = backup + +class GenericPlatform: + def __init__(self, device, io, default_crg_factory=None): + self.device = device + self.constraint_manager = ConstraintManager(io) + self.default_crg_factory = default_crg_factory + + def request(self, *args, **kwargs): + return self.constraint_manager.request(*args, **kwargs) + + def add_platform_command(self, *args, **kwargs): + return self.constraint_manager.add_platform_command(*args, **kwargs) + + def get_verilog(self, fragment, clock_domains=None): + # We may create a temporary clock/reset generator that would request pins. + # Save the constraint manager state so that such pin requests disappear + # at the end of this function. + backup = self.constraint_manager.save() + try: + # if none exists, create a default clock domain and drive it + if clock_domains is None: + if self.default_crg_factory is None: + raise NotImplementedError("No clock/reset generator defined by either platform or user") + crg = self.default_crg_factory(self) + frag = fragment + crg.get_fragment() + clock_domains = crg.get_clock_domains() + else: + frag = fragment + # generate Verilog + src, vns = verilog.convert(frag, self.constraint_manager.get_io_signals(), + clock_domains=clock_domains, return_ns=True) + # resolve signal names in constraints + sc = self.constraint_manager.get_sig_constraints() + named_sc = [(vns.get_name(sig), pins, others, resource) for sig, pins, others, resource in sc] + # resolve signal names in platform commands + pc = self.constraint_manager.get_platform_commands() + named_pc = [] + for template, args in pc: + name_dict = dict((k, vns.get_name(sig)) for k, sig in args.items()) + named_pc.append(template.format(**name_dict)) + finally: + self.constraint_manager.restore(backup) + return src, named_sc, named_pc + + def build(self, fragment, clock_domains=None): + raise NotImplementedError("GenericPlatform.build must be overloaded") diff --git a/mibuild/platforms/__init__.py b/mibuild/platforms/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mibuild/platforms/rhino.py b/mibuild/platforms/rhino.py new file mode 100644 index 000000000..9c562ba6c --- /dev/null +++ b/mibuild/platforms/rhino.py @@ -0,0 +1,131 @@ +from mibuild.generic_platform import * +from mibuild.xilinx_ise import XilinxISEPlatform, CRG_DS + +_io = [ + ("user_led", 0, Pins("Y3")), + ("user_led", 1, Pins("Y1")), + ("user_led", 2, Pins("W2")), + ("user_led", 3, Pins("W1")), + ("user_led", 4, Pins("V3")), + ("user_led", 5, Pins("V1")), + ("user_led", 6, Pins("U2")), + ("user_led", 7, Pins("U1")), + + ("clk100", 0, + Subsignal("p", Pins("B14"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")), + Subsignal("n", Pins("A14"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")) + ), + + ("gpio", 0, Pins("R8")), + + ("gpmc", 0, + Subsignal("clk", Pins("R26")), + Subsignal("a", Pins("N17", "N18", "L23", "L24", "N19", "N20", "N21", "N22", "P17", "P19")), + Subsignal("d", Pins("N23", "N24", "R18", "R19", "P21", "P22", "R20", "R21", "P24", "P26", "R23", "R24", "T22", "T23", "U23", "R25")), + Subsignal("we_n", Pins("W26")), + Subsignal("oe_n", Pins("AA25")), + Subsignal("ale_n", Pins("AA26")), + IOStandard("LVCMOS33")), + # Warning: CS are numbered 1-7 on ARM side and 0-6 on FPGA side. + # Numbers here are given on the FPGA side. + ("gpmc_ce_n", 0, Pins("V23"), IOStandard("LVCMOS33")), # nCS0 + ("gpmc_ce_n", 1, Pins("U25"), IOStandard("LVCMOS33")), # nCS1 + ("gpmc_ce_n", 2, Pins("W25"), IOStandard("LVCMOS33")), # nCS6 + ("gpmc_dmareq_n", 0, Pins("T24"), IOStandard("LVCMOS33")), # nCS2 + ("gpmc_dmareq_n", 1, Pins("T26"), IOStandard("LVCMOS33")), # nCS3 + ("gpmc_dmareq_n", 2, Pins("V24"), IOStandard("LVCMOS33")), # nCS4 + ("gpmc_dmareq_n", 3, Pins("V26"), IOStandard("LVCMOS33")), # nCS5 + + # FMC150 + ("fmc150_ctrl", 0, + Subsignal("spi_sclk", Pins("AE5")), + Subsignal("spi_data", Pins("AF5")), + + Subsignal("adc_sdo", Pins("U13")), + Subsignal("adc_en_n", Pins("AA15")), + Subsignal("adc_reset", Pins("V13")), + + Subsignal("cdce_sdo", Pins("AA8")), + Subsignal("cdce_en_n", Pins("Y9")), + Subsignal("cdce_reset_n", Pins("AB7")), + Subsignal("cdce_pd_n", Pins("AC6")), + Subsignal("cdce_pll_status", Pins("W7")), + Subsignal("cdce_ref_en", Pins("W8")), + + Subsignal("dac_sdo", Pins("W9")), + Subsignal("dac_en_n", Pins("W10")), + + Subsignal("mon_sdo", Pins("AC5")), + Subsignal("mon_en_n", Pins("AD6")), + Subsignal("mon_reset_n", Pins("AF6")), + Subsignal("mon_int_n", Pins("AD5")), + + Subsignal("pg_c2m", Pins("AA23"), IOStandard("LVCMOS33")) + ), + ("ti_dac", 0, # DAC3283 + Subsignal("dat_p", Pins("AA10", "AA9", "V11", "Y11", "W14", "Y12", "AD14", "AE13"), IOStandard("LVDS_25")), + Subsignal("dat_n", Pins("AB11", "AB9", "V10", "AA11", "Y13", "AA12", "AF14", "AF13"), IOStandard("LVDS_25")), + Subsignal("frame_p", Pins("AB13"), IOStandard("LVDS_25")), + Subsignal("frame_n", Pins("AA13"), IOStandard("LVDS_25")), + Subsignal("txenable", Pins("AB15"), IOStandard("LVCMOS25")) + ), + ("ti_adc", 0, # ADS62P49 + Subsignal("dat_a_p", Pins("AB14", "Y21", "W20", "AB22", "V18", "W17", "AA21")), + Subsignal("dat_a_n", Pins("AC14", "AA22", "Y20", "AC22", "W19", "W18", "AB21")), + Subsignal("dat_b_p", Pins("Y17", "U15", "AA19", "W16", "AA18", "Y15", "V14")), + Subsignal("dat_b_n", Pins("AA17", "V16", "AB19", "Y16", "AB17", "AA16", "V15")), + IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE") + ), + ("fmc150_clocks", 0, + Subsignal("dac_clk_p", Pins("V12"), IOStandard("LVDS_25")), + Subsignal("dac_clk_n", Pins("W12"), IOStandard("LVDS_25")), + Subsignal("adc_clk_p", Pins("AE15"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")), + Subsignal("adc_clk_n", Pins("AF15"), IOStandard("LVDS_25"), Misc("DIFF_TERM=TRUE")), + Subsignal("clk_to_fpga", Pins("W24"), IOStandard("LVCMOS25")) + ), + + ("fmc150_ext_trigger", 0, Pins("U26")), + + # Vermeer radar testbed + # TX path + ("pe43602", 0, + Subsignal("d", Pins("H8")), + Subsignal("clk", Pins("B3")), + Subsignal("le", Pins("F7")), + IOStandard("LVCMOS33") + ), + ("rfmd2081", 0, + Subsignal("enx", Pins("E5")), + Subsignal("sclk", Pins("G6")), + Subsignal("sdata", Pins("F5")), + Subsignal("sdatao", Pins("E6")), + IOStandard("LVCMOS33") + ), + # RX path + ("lmh6521", 0, + Subsignal("scsb", Pins("C5")), + Subsignal("sclk", Pins("G10")), + Subsignal("sdi", Pins("D5")), + Subsignal("sdo", Pins("F9")), + IOStandard("LVCMOS33") + ), + ("lmh6521", 1, + Subsignal("scsb", Pins("E10")), + Subsignal("sclk", Pins("A4")), + Subsignal("sdi", Pins("B4")), + Subsignal("sdo", Pins("H10")), + IOStandard("LVCMOS33") + ), + ("rffc5071", 0, + Subsignal("enx", Pins("A2")), + Subsignal("sclk", Pins("G9")), + Subsignal("sdata", Pins("H9")), + Subsignal("sdatao", Pins("A3")), + IOStandard("LVCMOS33") + ) +] + +class Platform(XilinxISEPlatform): + def __init__(self): + XilinxISEPlatform.__init__(self, "xc6slx150t-fgg676-3", _io, + lambda p: CRG_DS(p, "clk100", "gpio", 10.0)) diff --git a/mibuild/tools.py b/mibuild/tools.py new file mode 100644 index 000000000..565312a26 --- /dev/null +++ b/mibuild/tools.py @@ -0,0 +1,12 @@ +import os + +def mkdir_noerror(d): + try: + os.mkdir(d) + except OSError: + pass + +def write_to_file(filename, contents): + f = open(filename, "w") + f.write(contents) + f.close() diff --git a/mibuild/xilinx_ise.py b/mibuild/xilinx_ise.py new file mode 100644 index 000000000..ce82b383c --- /dev/null +++ b/mibuild/xilinx_ise.py @@ -0,0 +1,122 @@ +import os, struct, subprocess +from decimal import Decimal + +from migen.fhdl.structure import * + +from mibuild.generic_platform import * +from mibuild.crg import CRG, SimpleCRG +from mibuild import tools + +def _add_period_constraint(platform, clk, period): + 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): + SimpleCRG.__init__(self, platform, clk_name, rst_name) + _add_period_constraint(platform, self.cd.clk, period) + +class CRG_DS(CRG): + def __init__(self, platform, clk_name, rst_name, period): + self.cd = ClockDomain("sys") + self._clk = platform.request(clk_name) + platform.request(rst_name, None, self.cd.rst) + _add_period_constraint(platform, self._clk.p, period) + + def get_fragment(self): + ibufg = Instance("IBUFGDS", + Instance.Input("I", self._clk.p), + Instance.Input("IB", self._clk.n), + Instance.Output("O", self.cd.clk) + ) + return Fragment(instances=[ibufg]) + +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 = [_format_constraint(c) for c in ([Pins(pin)] + others)] + 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(device, sources, named_sc, named_pc, build_name, xilinx_install_path): + tools.write_to_file(build_name + ".ucf", _build_ucf(named_sc, named_pc)) + + prj_contents = "" + for s in sources: + prj_contents += s["type"] + " work " + s["path"] + "\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 +-ofn %s.ngc +-p %s""" % (build_name, build_name, device) + tools.write_to_file(build_name + ".xst", xst_contents) + + def is_valid_version(v): + try: + Decimal(v) + return os.path.isdir(os.path.join(xilinx_install_path, v)) + except: + return False + vers = [ver for ver in os.listdir(xilinx_install_path) if is_valid_version(ver)] + tools_version = max(vers) + bits = struct.calcsize("P")*8 + xilinx_settings_file = '%s/%s/ISE_DS/settings%d.sh' % (xilinx_install_path, tools_version, bits) + + build_script_contents = """# Autogenerated by mibuild + +set -e + +source {xilinx_settings_file} +xst -ifn {build_name}.xst +ngdbuild -uc {build_name}.ucf {build_name}.ngc +map -ol high -w {build_name}.ngd +par -ol high -w {build_name}.ncd {build_name}-routed.ncd +bitgen -g Binary:Yes -w {build_name}-routed.ncd {build_name}.bit +""".format(build_name=build_name, xilinx_settings_file=xilinx_settings_file) + 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 XilinxISEPlatform(GenericPlatform): + def build(self, fragment, clock_domains=None, build_dir="build", build_name="top", + xilinx_install_path="/opt/Xilinx"): + tools.mkdir_noerror(build_dir) + os.chdir(build_dir) + + v_src, named_sc, named_pc = self.get_verilog(fragment, clock_domains) + v_file = build_name + ".v" + tools.write_to_file(v_file, v_src) + sources = [{"type": "verilog", "path": v_file}] + _build(self.device, sources, named_sc, named_pc, build_name, xilinx_install_path) + + os.chdir("..")