Merge pull request #122 from daveshah1/trellis_ulx3s

Switch Trellis build to use LPF constraints; working on ULX3S
This commit is contained in:
enjoy-digital 2018-11-02 19:59:23 +01:00 committed by GitHub
commit 93c623251b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 99 deletions

View file

@ -36,6 +36,8 @@ _io = [
Subsignal("dm", Pins("U19 E20")), Subsignal("dm", Pins("U19 E20")),
IOStandard("LVCMOS33") IOStandard("LVCMOS33")
), ),
("wifi_gpio0", 0, Pins("L2"), IOStandard("LVCMOS33")),
] ]
@ -44,4 +46,4 @@ class Platform(LatticePlatform):
default_clk_period = 10 default_clk_period = 10
def __init__(self, **kwargs): def __init__(self, **kwargs):
LatticePlatform.__init__(self, "LFE5U-85F-6BG381C", _io, **kwargs) LatticePlatform.__init__(self, "LFE5U-45F-6BG381C", _io, **kwargs)

View file

@ -45,6 +45,12 @@ class _CRG(Module):
o_Z=new_sdram_ps_clk) o_Z=new_sdram_ps_clk)
sdram_ps_clk = new_sdram_ps_clk sdram_ps_clk = new_sdram_ps_clk
self.comb += self.cd_sys_ps.clk.eq(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): class BaseSoC(SoCSDRAM):

View file

@ -13,8 +13,6 @@ from litex.build.lattice import common
# TODO: # TODO:
# - add timing constraint support. # - add timing constraint support.
# - check/document attr_translate. # - check/document attr_translate.
# - use constraint file when prjtrellis will support it.
nextpnr_ecp5_architectures = { nextpnr_ecp5_architectures = {
"lfe5u-25f": "25k", "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): def yosys_import_sources(platform):
includes = "" includes = ""
reads = [] reads = []
@ -40,93 +69,6 @@ def yosys_import_sources(platform):
return "\n".join(reads) 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: class LatticePrjTrellisToolchain:
attr_translate = { attr_translate = {
# FIXME: document # FIXME: document
@ -156,22 +98,20 @@ class LatticePrjTrellisToolchain:
fragment = fragment.get_fragment() fragment = fragment.get_fragment()
platform.finalize(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_file = build_name + ".v"
top_output.write(top_file) top_output.write(top_file)
platform.add_source(top_file)
# insert constraints and trellis_io to generated verilog # generate constraints
prjtrellis_top_file = build_name + "_prjtrellis.v" tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc))
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 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 prjtrellis_{build_name}" "synth_ecp5 -nomux -json {build_name}.json -top {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)
@ -187,7 +127,7 @@ class LatticePrjTrellisToolchain:
build_script_file = "build_" + build_name + ".sh" build_script_file = "build_" + build_name + ".sh"
build_script_contents = [ build_script_contents = [
"yosys -q -l {build_name}.rpt {build_name}.ys", "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" "ecppack {build_name}.config {build_name}.bit"
] ]