build/lattice/prjtrellis: modify generated verilog instead of creating a wrapper, handle inouts.
nextpnr expects TRELLIS_IO on all ios, it's not possible to ensure that with a wrapper. We now just modify the generated verilog to insert the io constraints and TRELLIS_IOs.
This commit is contained in:
parent
2243f628f7
commit
6048a5291c
|
@ -12,8 +12,8 @@ from litex.build.lattice import common
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# - add timing constraint support.
|
# - add timing constraint support.
|
||||||
# - add inout support to iowrapper.
|
|
||||||
# - check/document attr_translate.
|
# - check/document attr_translate.
|
||||||
|
# - use constraint file when prjtrellis will support it.
|
||||||
|
|
||||||
|
|
||||||
nextpnr_ecp5_architectures = {
|
nextpnr_ecp5_architectures = {
|
||||||
|
@ -40,21 +40,22 @@ def yosys_import_sources(platform):
|
||||||
return "\n".join(reads)
|
return "\n".join(reads)
|
||||||
|
|
||||||
|
|
||||||
def generate_prjtrellis_iowrapper(platform, vns):
|
def generate_prjtrellis_top(top_file, platform, vns):
|
||||||
|
# resolve ios directions / types
|
||||||
ios, _ = platform.resolve_signals(vns)
|
ios, _ = platform.resolve_signals(vns)
|
||||||
ios_direction = {}
|
ios_direction = {}
|
||||||
# resolve ios directions
|
ios_type = {}
|
||||||
cm = platform.constraint_manager
|
cm = platform.constraint_manager
|
||||||
for io_name, io_pins, _, _ in ios:
|
for io_name, io_pins, _, _ in ios:
|
||||||
for cm_sig, cm_pins, _, _ in cm.get_sig_constraints():
|
for cm_sig, cm_pins, _, _ in cm.get_sig_constraints():
|
||||||
if io_pins == cm_pins:
|
if io_pins == cm_pins:
|
||||||
ios_direction[io_name] = cm_sig.direction
|
ios_direction[io_name] = cm_sig.direction
|
||||||
|
ios_type[io_name] = cm_sig.type
|
||||||
last_io_name = io_name
|
last_io_name = io_name
|
||||||
|
|
||||||
iowrapper_contents = []
|
# prjtrellis module / ios declaration
|
||||||
iowrapper_contents.append("module {build_name}_iowrapper(")
|
top_contents = []
|
||||||
|
top_contents.append("module prjtrellis_{build_name}(")
|
||||||
# ios declaration
|
|
||||||
ios_declaration = ""
|
ios_declaration = ""
|
||||||
for io_name, io_pins, io_others, _ in ios:
|
for io_name, io_pins, io_others, _ in ios:
|
||||||
for io_other in io_others:
|
for io_other in io_others:
|
||||||
|
@ -64,20 +65,20 @@ def generate_prjtrellis_iowrapper(platform, vns):
|
||||||
ios_declaration += "(* LOC=\"{}\" *) (* IO_TYPE=\"{}\" *)\n".format(io_pin, io_standard)
|
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 += "\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 ""
|
ios_declaration += ",\n" if io_name != last_io_name or (i != len(io_pins) - 1) else ""
|
||||||
iowrapper_contents.append(ios_declaration)
|
top_contents.append(ios_declaration)
|
||||||
iowrapper_contents.append(");\n")
|
top_contents.append(");\n")
|
||||||
|
|
||||||
# wires declaration
|
# top signals declaration
|
||||||
wires_declaration = ""
|
signals_declaration = ""
|
||||||
for io_name, io_pins, _, _ in ios:
|
for io_name, io_pins, _, _ in ios:
|
||||||
wires_declaration += "wire "
|
signals_declaration += ios_type[io_name] + " "
|
||||||
if len(io_pins) > 1:
|
if len(io_pins) > 1:
|
||||||
wires_declaration += "[{}:0] ".format(len(io_pins) - 1)
|
signals_declaration += "[{}:0] ".format(len(io_pins) - 1)
|
||||||
wires_declaration += io_name
|
signals_declaration += io_name
|
||||||
wires_declaration += ";\n"
|
signals_declaration += ";\n"
|
||||||
iowrapper_contents.append(wires_declaration)
|
top_contents.append(signals_declaration)
|
||||||
|
|
||||||
# trellis_io declaration
|
# trellis_ios declaration
|
||||||
trellis_io_declaration = ""
|
trellis_io_declaration = ""
|
||||||
for io_name, io_pins, io_others, _ in ios:
|
for io_name, io_pins, io_others, _ in ios:
|
||||||
for i, io_pin in enumerate(io_pins):
|
for i, io_pin in enumerate(io_pins):
|
||||||
|
@ -92,30 +93,38 @@ def generate_prjtrellis_iowrapper(platform, vns):
|
||||||
io_name + "_buf" + str(i), io_name + "_" + io_suffix, io_name + "[" + str(i) + "]")
|
io_name + "_buf" + str(i), io_name + "_" + io_suffix, io_name + "[" + str(i) + "]")
|
||||||
else:
|
else:
|
||||||
pass # handled by Migen's Tristate
|
pass # handled by Migen's Tristate
|
||||||
iowrapper_contents.append(trellis_io_declaration)
|
top_contents.append(trellis_io_declaration)
|
||||||
|
|
||||||
# top declaration
|
# top_recopy:
|
||||||
top_declaration = "{build_name} _{build_name}(\n"
|
# - skip module definition.
|
||||||
for io_name, io_pins, _, _ in ios:
|
# - use ios names for inouts.
|
||||||
if ios_direction[io_name] == "inout":
|
def replace_inouts(l):
|
||||||
if len(io_pins) > 1:
|
r = l
|
||||||
io_concat_name = "{{"
|
for io_name, io_pins, _, _ in ios:
|
||||||
io_concat_name += ",".join([io_name + "_io" + str(i) for i in range(len(io_pins))])
|
if ios_direction[io_name] == "inout":
|
||||||
io_concat_name += "}}"
|
if len(io_pins) > 1:
|
||||||
top_declaration += "\t." + io_name + "(" + io_concat_name + ")"
|
for i in range(len(io_pins)):
|
||||||
else:
|
r = r.replace(io_name + "[" + str(i) + "]", io_name + "_io" + str(i))
|
||||||
top_declaration += "\t." + io_name + "(" + io_name + "_io)"
|
else:
|
||||||
else:
|
r = r.replace(io_name, io_name + "_io")
|
||||||
top_declaration += "\t." + io_name + "(" + io_name + ")"
|
return r
|
||||||
top_declaration += ",\n" if io_name != last_io_name else "\n"
|
|
||||||
top_declaration += ");\n"
|
|
||||||
iowrapper_contents.append(top_declaration)
|
|
||||||
|
|
||||||
iowrapper_contents.append("endmodule")
|
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()
|
||||||
|
|
||||||
iowrapper_contents = "\n".join(iowrapper_contents)
|
top_contents = "\n".join(top_contents)
|
||||||
|
|
||||||
return iowrapper_contents
|
return top_contents
|
||||||
|
|
||||||
|
|
||||||
class LatticePrjTrellisToolchain:
|
class LatticePrjTrellisToolchain:
|
||||||
|
@ -147,24 +156,22 @@ class LatticePrjTrellisToolchain:
|
||||||
fragment = fragment.get_fragment()
|
fragment = fragment.get_fragment()
|
||||||
platform.finalize(fragment)
|
platform.finalize(fragment)
|
||||||
|
|
||||||
v_output = platform.get_verilog(fragment)
|
top_output = platform.get_verilog(fragment)
|
||||||
v_file = build_name + ".v"
|
top_file = build_name + ".v"
|
||||||
v_output.write(v_file)
|
top_output.write(top_file)
|
||||||
platform.add_source(v_file)
|
|
||||||
|
|
||||||
# generate iowrapper (with constraints and trellis_ios)
|
# insert constraints and trellis_io to generated verilog
|
||||||
# FIXME: remove when prjtrellis will support constraint files
|
prjtrellis_top_file = build_name + "_prjtrellis.v"
|
||||||
iowrapper_file = build_name + "_iowrapper.v"
|
prjtrellis_top_contents = generate_prjtrellis_top(top_file, platform, top_output.ns)
|
||||||
iowrapper_contents = generate_prjtrellis_iowrapper(platform, v_output.ns)
|
prjtrellis_top_contents = prjtrellis_top_contents.format(build_name=build_name)
|
||||||
iowrapper_contents = iowrapper_contents.format(build_name=build_name)
|
tools.write_to_file(prjtrellis_top_file, prjtrellis_top_contents)
|
||||||
tools.write_to_file(iowrapper_file, iowrapper_contents)
|
platform.add_source(prjtrellis_top_file)
|
||||||
platform.add_source(iowrapper_file)
|
|
||||||
|
|
||||||
# generate yosys script
|
# generate yosys script
|
||||||
yosys_script_file = build_name + ".ys"
|
yosys_script_file = build_name + ".ys"
|
||||||
yosys_script_contents = [
|
yosys_script_contents = [
|
||||||
yosys_import_sources(platform),
|
yosys_import_sources(platform),
|
||||||
"synth_ecp5 -nomux -json {build_name}.json -top {build_name}_iowrapper"
|
"synth_ecp5 -nomux -json {build_name}.json -top prjtrellis_{build_name}"
|
||||||
]
|
]
|
||||||
yosys_script_contents = "\n".join(yosys_script_contents)
|
yosys_script_contents = "\n".join(yosys_script_contents)
|
||||||
yosys_script_contents = yosys_script_contents.format(build_name=build_name)
|
yosys_script_contents = yosys_script_contents.format(build_name=build_name)
|
||||||
|
|
|
@ -206,6 +206,7 @@ def _printheader(f, ios, name, ns, attr_translate,
|
||||||
attr = _printattr(sig.attr, attr_translate)
|
attr = _printattr(sig.attr, attr_translate)
|
||||||
if attr:
|
if attr:
|
||||||
r += "\t" + attr
|
r += "\t" + attr
|
||||||
|
sig.type = "wire"
|
||||||
if sig in inouts:
|
if sig in inouts:
|
||||||
sig.direction = "inout"
|
sig.direction = "inout"
|
||||||
r += "\tinout " + _printsig(ns, sig)
|
r += "\tinout " + _printsig(ns, sig)
|
||||||
|
@ -214,6 +215,7 @@ def _printheader(f, ios, name, ns, attr_translate,
|
||||||
if sig in wires:
|
if sig in wires:
|
||||||
r += "\toutput " + _printsig(ns, sig)
|
r += "\toutput " + _printsig(ns, sig)
|
||||||
else:
|
else:
|
||||||
|
sig.type = "reg"
|
||||||
r += "\toutput reg " + _printsig(ns, sig)
|
r += "\toutput reg " + _printsig(ns, sig)
|
||||||
else:
|
else:
|
||||||
sig.direction = "input"
|
sig.direction = "input"
|
||||||
|
|
Loading…
Reference in New Issue