From 0c1d8d59935c2ddca8f44e961c975640736b8096 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 31 Oct 2018 11:43:39 +0000 Subject: [PATCH 1/3] trellis: Switch to using LPF for constraints Signed-off-by: David Shah --- litex/build/lattice/prjtrellis.py | 133 ++++++++---------------------- 1 file changed, 36 insertions(+), 97 deletions(-) diff --git a/litex/build/lattice/prjtrellis.py b/litex/build/lattice/prjtrellis.py index d019e1479..e27ae1c6a 100644 --- a/litex/build/lattice/prjtrellis.py +++ b/litex/build/lattice/prjtrellis.py @@ -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" ] From 8404434956c4987d4730b7ba48c843a937194808 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 31 Oct 2018 12:27:05 +0000 Subject: [PATCH 2/3] Fix Trellis build; ULX3S demo boots to BIOS Signed-off-by: David Shah --- litex/boards/platforms/ulx3s.py | 4 +++- litex/boards/targets/ulx3s.py | 4 ++++ litex/build/lattice/prjtrellis.py | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/litex/boards/platforms/ulx3s.py b/litex/boards/platforms/ulx3s.py index e1ccaa31e..319596d29 100644 --- a/litex/boards/platforms/ulx3s.py +++ b/litex/boards/platforms/ulx3s.py @@ -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) diff --git a/litex/boards/targets/ulx3s.py b/litex/boards/targets/ulx3s.py index 1e1a30063..6d8a4e077 100755 --- a/litex/boards/targets/ulx3s.py +++ b/litex/boards/targets/ulx3s.py @@ -46,6 +46,10 @@ class _CRG(Module): sdram_ps_clk = new_sdram_ps_clk self.comb += self.cd_sys_ps.clk.eq(sdram_ps_clk) + # Stop ESP32 from resetting FPGA + wifi_gpio0 = platform.request("wifi_gpio0") + self.comb += wifi_gpio0.eq(1) + class BaseSoC(SoCSDRAM): def __init__(self, **kwargs): diff --git a/litex/build/lattice/prjtrellis.py b/litex/build/lattice/prjtrellis.py index e27ae1c6a..ec983505f 100644 --- a/litex/build/lattice/prjtrellis.py +++ b/litex/build/lattice/prjtrellis.py @@ -98,10 +98,11 @@ 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) # generate constraints tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc)) From 0729b3a059b1991405de8b345b3130499c874ef5 Mon Sep 17 00:00:00 2001 From: David Shah Date: Wed, 31 Oct 2018 13:29:35 +0000 Subject: [PATCH 3/3] ulx3s: Connect SDRAM clock Signed-off-by: David Shah --- litex/boards/targets/ulx3s.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litex/boards/targets/ulx3s.py b/litex/boards/targets/ulx3s.py index 6d8a4e077..af1aa19e4 100755 --- a/litex/boards/targets/ulx3s.py +++ b/litex/boards/targets/ulx3s.py @@ -45,6 +45,8 @@ 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")