trellis: Switch to using LPF for constraints

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2018-10-31 11:43:39 +00:00
parent 445c49400f
commit 0c1d8d5993

View file

@ -13,8 +13,6 @@ from litex.build.lattice import common
# TODO:
# - add timing constraint support.
# - check/document attr_translate.
# - use constraint file when prjtrellis will support it.
nextpnr_ecp5_architectures = {
"lfe5u-25f": "25k",
@ -29,6 +27,37 @@ nextpnr_ecp5_architectures = {
}
def _format_constraint(c):
if isinstance(c, Pins):
return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"")
elif isinstance(c, IOStandard):
return ("IOBUF PORT ", " IO_TYPE=" + c.name)
elif isinstance(c, Misc):
return c.misc
def _format_lpf(signame, pin, others, resname):
fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)]
r = ""
for pre, suf in fmt_c:
r += pre + "\"" + signame + "\"" + suf + ";\n"
return r
def _build_lpf(named_sc, named_pc):
r = "BLOCK RESETPATHS;\n"
r += "BLOCK ASYNCPATHS;\n"
for sig, pins, others, resname in named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
r += _format_lpf(sig + "[" + str(i) + "]", p, others, resname)
else:
r += _format_lpf(sig, pins[0], others, resname)
if named_pc:
r += "\n" + "\n\n".join(named_pc)
return r
def yosys_import_sources(platform):
includes = ""
reads = []
@ -40,93 +69,6 @@ def yosys_import_sources(platform):
return "\n".join(reads)
def generate_prjtrellis_top(top_file, platform, vns):
# resolve ios directions / types
ios, _ = platform.resolve_signals(vns)
ios_direction = {}
ios_type = {}
cm = platform.constraint_manager
for io_name, io_pins, _, _ in ios:
for cm_sig, cm_pins, _, _ in cm.get_sig_constraints():
if io_pins == cm_pins:
ios_direction[io_name] = cm_sig.direction
ios_type[io_name] = cm_sig.type
last_io_name = io_name
# prjtrellis module / ios declaration
top_contents = []
top_contents.append("module prjtrellis_{build_name}(")
ios_declaration = ""
for io_name, io_pins, io_others, _ in ios:
for io_other in io_others:
if isinstance(io_other, IOStandard):
io_standard = io_other.name
for i, io_pin in enumerate(io_pins):
ios_declaration += "(* LOC=\"{}\" *) (* IO_TYPE=\"{}\" *)\n".format(io_pin, io_standard)
ios_declaration += "\t" + ios_direction[io_name] + " " + io_name + "_io" + (str(i) if len(io_pins) > 1 else "")
ios_declaration += ",\n" if io_name != last_io_name or (i != len(io_pins) - 1) else ""
top_contents.append(ios_declaration)
top_contents.append(");\n")
# top signals declaration
signals_declaration = ""
for io_name, io_pins, _, _ in ios:
signals_declaration += ios_type[io_name] + " "
if len(io_pins) > 1:
signals_declaration += "[{}:0] ".format(len(io_pins) - 1)
signals_declaration += io_name
signals_declaration += ";\n"
top_contents.append(signals_declaration)
# trellis_ios declaration
trellis_io_declaration = ""
for io_name, io_pins, io_others, _ in ios:
for i, io_pin in enumerate(io_pins):
io_suffix = "io" + str(i) if len(io_pins) > 1 else "io"
if ios_direction[io_name] == "input":
trellis_io_declaration += \
"TRELLIS_IO #(.DIR(\"INPUT\")) {} (.B({}), .O({}));\n".format(
io_name + "_buf" + str(i), io_name + "_" + io_suffix, io_name + "[" + str(i) + "]")
elif ios_direction[io_name] == "output":
trellis_io_declaration += \
"TRELLIS_IO #(.DIR(\"OUTPUT\")) {} (.B({}), .I({}));\n".format(
io_name + "_buf" + str(i), io_name + "_" + io_suffix, io_name + "[" + str(i) + "]")
else:
pass # handled by Migen's Tristate
top_contents.append(trellis_io_declaration)
# top_recopy:
# - skip module definition.
# - use ios names for inouts.
def replace_inouts(l):
r = l
for io_name, io_pins, _, _ in ios:
if ios_direction[io_name] == "inout":
if len(io_pins) > 1:
for i in range(len(io_pins)):
r = r.replace(io_name + "[" + str(i) + "]", io_name + "_io" + str(i))
else:
r = r.replace(io_name, io_name + "_io")
return r
skip = True
f = open(top_file, "r")
for l in f:
if not skip:
l = l.replace("\n", "")
l = l.replace("{", "{{")
l = l.replace("}", "}}")
l = replace_inouts(l)
top_contents.append(l)
if ");" in l:
skip = False
f.close()
top_contents = "\n".join(top_contents)
return top_contents
class LatticePrjTrellisToolchain:
attr_translate = {
# FIXME: document
@ -157,21 +99,18 @@ class LatticePrjTrellisToolchain:
platform.finalize(fragment)
top_output = platform.get_verilog(fragment)
named_sc, named_pc = platform.resolve_signals(top_output.ns)
top_file = build_name + ".v"
top_output.write(top_file)
# insert constraints and trellis_io to generated verilog
prjtrellis_top_file = build_name + "_prjtrellis.v"
prjtrellis_top_contents = generate_prjtrellis_top(top_file, platform, top_output.ns)
prjtrellis_top_contents = prjtrellis_top_contents.format(build_name=build_name)
tools.write_to_file(prjtrellis_top_file, prjtrellis_top_contents)
platform.add_source(prjtrellis_top_file)
# generate constraints
tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc))
# generate yosys script
yosys_script_file = build_name + ".ys"
yosys_script_contents = [
yosys_import_sources(platform),
"synth_ecp5 -nomux -json {build_name}.json -top prjtrellis_{build_name}"
"synth_ecp5 -nomux -json {build_name}.json -top {build_name}"
]
yosys_script_contents = "\n".join(yosys_script_contents)
yosys_script_contents = yosys_script_contents.format(build_name=build_name)
@ -187,7 +126,7 @@ class LatticePrjTrellisToolchain:
build_script_file = "build_" + build_name + ".sh"
build_script_contents = [
"yosys -q -l {build_name}.rpt {build_name}.ys",
"nextpnr-ecp5 --json {build_name}.json --textcfg {build_name}.config --basecfg {basecfg} --{architecture}",
"nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --basecfg {basecfg} --{architecture}",
"ecppack {build_name}.config {build_name}.bit"
]