mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
Merge pull request #122 from daveshah1/trellis_ulx3s
Switch Trellis build to use LPF constraints; working on ULX3S
This commit is contained in:
commit
93c623251b
3 changed files with 47 additions and 99 deletions
|
@ -36,6 +36,8 @@ _io = [
|
|||
Subsignal("dm", Pins("U19 E20")),
|
||||
IOStandard("LVCMOS33")
|
||||
),
|
||||
|
||||
("wifi_gpio0", 0, Pins("L2"), IOStandard("LVCMOS33")),
|
||||
]
|
||||
|
||||
|
||||
|
@ -44,4 +46,4 @@ class Platform(LatticePlatform):
|
|||
default_clk_period = 10
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
LatticePlatform.__init__(self, "LFE5U-85F-6BG381C", _io, **kwargs)
|
||||
LatticePlatform.__init__(self, "LFE5U-45F-6BG381C", _io, **kwargs)
|
||||
|
|
|
@ -45,6 +45,12 @@ class _CRG(Module):
|
|||
o_Z=new_sdram_ps_clk)
|
||||
sdram_ps_clk = new_sdram_ps_clk
|
||||
self.comb += self.cd_sys_ps.clk.eq(sdram_ps_clk)
|
||||
sdram_clock = platform.request("sdram_clock")
|
||||
self.comb += sdram_clock.eq(sdram_ps_clk)
|
||||
|
||||
# Stop ESP32 from resetting FPGA
|
||||
wifi_gpio0 = platform.request("wifi_gpio0")
|
||||
self.comb += wifi_gpio0.eq(1)
|
||||
|
||||
|
||||
class BaseSoC(SoCSDRAM):
|
||||
|
|
|
@ -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
|
||||
|
@ -156,22 +98,20 @@ class LatticePrjTrellisToolchain:
|
|||
fragment = fragment.get_fragment()
|
||||
platform.finalize(fragment)
|
||||
|
||||
top_output = platform.get_verilog(fragment)
|
||||
top_output = platform.get_verilog(fragment, name=build_name)
|
||||
named_sc, named_pc = platform.resolve_signals(top_output.ns)
|
||||
top_file = build_name + ".v"
|
||||
top_output.write(top_file)
|
||||
platform.add_source(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 +127,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"
|
||||
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue