Initial version

This commit is contained in:
Sebastien Bourdeauducq 2013-02-07 22:07:30 +01:00
commit fb5130fc1f
7 changed files with 492 additions and 0 deletions

0
mibuild/__init__.py Normal file
View File

18
mibuild/crg.py Normal file
View File

@ -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)

209
mibuild/generic_platform.py Normal file
View File

@ -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")

View File

131
mibuild/platforms/rhino.py Normal file
View File

@ -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))

12
mibuild/tools.py Normal file
View File

@ -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()

122
mibuild/xilinx_ise.py Normal file
View File

@ -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("..")