Initial version
This commit is contained in:
commit
fb5130fc1f
|
@ -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)
|
|
@ -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")
|
|
@ -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))
|
|
@ -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()
|
|
@ -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("..")
|
Loading…
Reference in New Issue