Initial version
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Thu, 7 Feb 2013 21:07:30 +0000 (22:07 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Thu, 7 Feb 2013 21:07:30 +0000 (22:07 +0100)
mibuild/__init__.py [new file with mode: 0644]
mibuild/crg.py [new file with mode: 0644]
mibuild/generic_platform.py [new file with mode: 0644]
mibuild/platforms/__init__.py [new file with mode: 0644]
mibuild/platforms/rhino.py [new file with mode: 0644]
mibuild/tools.py [new file with mode: 0644]
mibuild/xilinx_ise.py [new file with mode: 0644]

diff --git a/mibuild/__init__.py b/mibuild/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/mibuild/crg.py b/mibuild/crg.py
new file mode 100644 (file)
index 0000000..0d4dcfb
--- /dev/null
@@ -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 (file)
index 0000000..3877552
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
diff --git a/mibuild/platforms/rhino.py b/mibuild/platforms/rhino.py
new file mode 100644 (file)
index 0000000..9c562ba
--- /dev/null
@@ -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 (file)
index 0000000..565312a
--- /dev/null
@@ -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 (file)
index 0000000..ce82b38
--- /dev/null
@@ -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("..")