diff --git a/litex_boards/platforms/logicbone.py b/litex_boards/platforms/logicbone.py new file mode 100644 index 0000000..c1cfc93 --- /dev/null +++ b/litex_boards/platforms/logicbone.py @@ -0,0 +1,195 @@ +# This file is Copyright (c) 2020 Owen Kirby +# License: BSD +# +# Logicbone ECP5: +# - Design files: https://github.com/oskirby/logicbone +# - Bootloader: https://github.com/oskirby/tinydfu-bootloader +# + +from litex.build.generic_platform import * +from litex.build.lattice import LatticePlatform +from litex.build.dfu import DFUProg + +# IOs ---------------------------------------------------------------------------------------------- + +_io_rev0 = [ + ("refclk", 0, Pins("M19"), IOStandard("LVCMOS18")), + ("rst_n", 0, Pins("C17"), IOStandard("LVCMOS33")), + ("user_btn", 0, Pins("U2"), IOStandard("LVCMOS33")), + + ("user_led", 0, Pins("D16"), IOStandard("LVCMOS33")), + ("user_led", 1, Pins("C15"), IOStandard("LVCMOS33")), + ("user_led", 2, Pins("C13"), IOStandard("LVCMOS33")), + ("user_led", 3, Pins("B13"), IOStandard("LVCMOS33")), + + ("ddram", 0, + Subsignal("a", Pins( + "D5 F4 B3 F3 E5 C3 C4 A5", + "A3 B5 G3 F5 D2 A4 D3 E3"), + IOStandard("SSTL135_I")), + Subsignal("ba", Pins("B4 H5 N2"), IOStandard("SSTL135_I")), + Subsignal("ras_n", Pins("L1"), IOStandard("SSTL135_I")), + Subsignal("cas_n", Pins("M1"), IOStandard("SSTL135_I")), + Subsignal("we_n", Pins("E4"), IOStandard("SSTL135_I")), + Subsignal("cs_n", Pins("M3"), IOStandard("SSTL135_I")), + #Subsignal("dm", Pins("L4 J5"), IOStandard("SSTL135_I")), + Subsignal("dm", Pins("L5 H3"), IOStandard("SSTL135_I")), # HACK: I broke the DM pins, so we'll use some NC pins instead. + Subsignal("dq", Pins( + "G2 K1 F1 K3 H2 J3 G1 H1", + "B1 E1 A2 F2 C1 E2 C2 D1"), + IOStandard("SSTL135_I"), + Misc("TERMINATION=75")), + Subsignal("dqs_p", Pins("K2 H4"), IOStandard("SSTL135D_I"), + Misc("TERMINATION=OFF"), + Misc("DIFFRESISTOR=100")), + Subsignal("clk_p", Pins("M4"), IOStandard("SSTL135D_I")), + Subsignal("cke", Pins("K4"), IOStandard("SSTL135_I")), + Subsignal("odt", Pins("C5"), IOStandard("SSTL135_I")), + Subsignal("reset_n", Pins("P1"), IOStandard("SSTL135_I")), + Misc("SLEWRATE=FAST"), + ), + + ("usb", 0, + Subsignal("d_p", Pins("B12")), + Subsignal("d_n", Pins("C12")), + Subsignal("pullup", Pins("C16")), + IOStandard("LVCMOS33") + ), + + ("serial", 0, + Subsignal("rx", Pins("B6"), IOStandard("LVCMOS33")), + Subsignal("tx", Pins("A7"), IOStandard("LVCMOS33")), + ), + + ("spiflash4x", 0, + Subsignal("cs_n", Pins("R2"), IOStandard("LVCMOS33")), + #Subsignal("clk", Pins("U3"), IOStandard("LVCMOS33")), # Note: CLK is bound using USRMCLK block + Subsignal("dq", Pins("W2 V2 Y2 W1"), IOStandard("LVCMOS33")), + ), + ("spiflash", 0, + Subsignal("cs_n", Pins("R2"), IOStandard("LVCMOS33")), + #Subsignal("clk", Pins("U3"), IOStandard("LVCMOS33")), # Note: CLK is bound using USRMCLK block + Subsignal("miso", Pins("V2"), IOStandard("LVCMOS33")), + Subsignal("mosi", Pins("W2"), IOStandard("LVCMOS33")), + Subsignal("wp", Pins("Y2"), IOStandard("LVCMOS33")), + Subsignal("hold", Pins("W1"), IOStandard("LVCMOS33")), + ), + + ("sdcard", 0, + Subsignal("clk", Pins("E11")), + Subsignal("cmd", Pins("D15"), Misc("PULLMODE=UP")), + Subsignal("data", Pins("D13 E13 E15 E14"), Misc("PULLMODE=UP")), + Subsignal("cd", Pins("D14"), Misc("PULLMODE=UP")), + IOStandard("LVCMOS33"), Misc("SLEW=FAST") + ), + + ("spisdcard", 0, + Subsignal("clk", Pins("E11")), + Subsignal("mosi", Pins("D15"), Misc("PULLMODE=UP")), + Subsignal("cs_n", Pins("E14"), Misc("PULLMODE=UP")), + Subsignal("miso", Pins("D13"), Misc("PULLMODE=UP")), + Misc("SLEW=FAST"), + IOStandard("LVCMOS33"), + ), + + ("i2c", 0, + Subsignal("sda", Pins("V1")), + Subsignal("scl", Pins("U1")), + IOStandard("LVCMOS33") + ), + + ("eth_clocks", 0, + Subsignal("tx", Pins("A15")), + Subsignal("rx", Pins("B18")), + Subsignal("ref", Pins("A19")), + IOStandard("LVCMOS33") + ), + + ("eth", 0, + #Subsignal("rst_n", Pins("U17")), # Stolen for SYS_RESETn on prototypes. + Subsignal("int_n", Pins("B20"), Misc("PULLMODE=UP")), # HACK: Should have a pullup on the board. + Subsignal("mdio", Pins("B19"), Misc("PULLMODE=UP")), # HACK: Should have a pullup on the board. + Subsignal("mdc", Pins("D12")), + Subsignal("tx_ctl", Pins("B15")), + Subsignal("tx_data", Pins("A12 A13 C14 A14")), + Subsignal("rx_ctl", Pins("A18")), + Subsignal("rx_data", Pins("B17 A17 B16 A16")), + IOStandard("LVCMOS33") + ), +] + +# Connectors --------------------------------------------------------------------------------------- + +_connectors_rev0 = [ + ("P8", + "None", # No pin 0 + "None", "None", # GND + "C20", "D19", # P8_LVDS1 + "D20", "E19", # P8_LVDS2 + "E20", "F19", # P8_LVDS3 + "F20", "G20", # P8_LVDS4 + "None", "None", + "None", "None", + "None", "None", + "None", "None", + "None", "None", + "None", "None", + "G19", "H20", # P8_LVDS5 + "J20", "K20", # P8_LVDS6 + "C18", "D17", # P8_LVDS7 + "D18", "E17", # P8_LVDS8 + "E18", "F18", # P8_LVDS9 + "F17", "G18", # P8_LVDS10 + "E16", "F16", # P8_LVDS11 + "G16", "H16", # P8_LVDS12 + "J17", "J16", # P8_LVDS13 + "H18", "H17", # P8_LVDS14 + "J19", "K19", # P8_LVDS15 + "J18", "K18"), # P8_LVDS16 + ("P9", + "None", # No pin 0 + "None", "None", # GND + "None", "None", # VCC3V3 + "None", "None", # CAPE_5V + "None", "None", # VBUS + "None", "None", # PWR_BUTTON, SYS_RESETn + "A11", "B11", # GPIO11, GPIO12 + "A10", "C10", # GPIO13, GPIO14 + "A9", "B9", # GPIO15, GPIO16 + "C11", "A8", # GPIO17, GPIO18 + "None", "None", # I2C Bus + "D9", "C8", # GPIO21, GPIO22 + "B8", "A7", # GPIO23, GPIO24 + "A6", "B6", # GPIO25, GPIO26 + "D8", "C7", # GPIO27, P9_SPI_CSEL + "D7", "C6", # P9_SPI_D0, P9_SPI_D1 + "D6", "None", # P9_SPI_SCLK, VADC + "None", "None", # AIN4, GNDA + "None", "None", # AIN6, AIN5 + "None", "None", # AIN2, AIN3 + "None", "None", # AIN0, AIN1 + "B10", "E10", # CLKOUT, GPIO42 + "None", "None", # GND + "None", "None") # GND +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(LatticePlatform): + default_clk_name = "refclk" + default_clk_period = 1e9/25e6 + + def __init__(self, revision="rev0", device="45F", **kwargs): + assert revision in ["rev0"] + self.revision = revision + io = {"rev0": _io_rev0 }[revision] + connectors = {"rev0": _connectors_rev0 }[revision] + LatticePlatform.__init__(self, f"LFE5UM5G-{device}-8BG381C", io, connectors, **kwargs) + + def create_programmer(self): + return DFUProg(vid="1d50", pid="6130") + + def do_finalize(self, fragment): + LatticePlatform.do_finalize(self, fragment) + self.add_period_constraint(self.lookup_request("refclk", loose=True), 1e9/25e6) + self.add_period_constraint(self.lookup_request("eth_clocks:rx", loose=True), 1e9/125e6) diff --git a/litex_boards/targets/logicbone.py b/litex_boards/targets/logicbone.py new file mode 100644 index 0000000..46289ff --- /dev/null +++ b/litex_boards/targets/logicbone.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 + +# This file is Copyright (c) 2020 Owen Kirby +# License: BSD + +import os +import sys +import argparse + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex_boards.platforms import logicbone + +from litex.build.lattice.trellis import trellis_args, trellis_argdict + +from litex.soc.cores.clock import * +from litex.soc.integration.soc_core import * +from litex.soc.integration.soc_sdram import * +from litex.soc.integration.builder import * +from litex.soc.cores.led import LedChaser + +from litedram.modules import MT41K512M16 +from litedram.phy import ECP5DDRPHY +from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII + +# _CRG --------------------------------------------------------------------------------------------- + +class _CRG(Module): + def __init__(self, platform, sys_clk_freq, with_usb_pll=False): + self.clock_domains.cd_init = ClockDomain() + self.clock_domains.cd_por = ClockDomain(reset_less=True) + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_sys2x = ClockDomain() + self.clock_domains.cd_sys2x_i = ClockDomain(reset_less=True) + self.clock_domains.cd_sys2x_eb = ClockDomain(reset_less=True) + self.clock_domains.cd_clk10 = ClockDomain() + + + # # # + + self.stop = Signal() + + # Clk / Rst + clk25 = platform.request("refclk") + + # Power on reset + por_count = Signal(16, reset=2**16-1) + por_done = Signal() + self.comb += self.cd_por.clk.eq(ClockSignal()) + self.comb += por_done.eq(por_count == 0) + self.sync.por += If(~por_done, por_count.eq(por_count - 1)) + + # PLL + sys2x_clk_ecsout = Signal() + self.submodules.pll = pll = ECP5PLL() + pll.register_clkin(clk25, 25e6) + pll.create_clkout(self.cd_sys2x_i, 2*sys_clk_freq) + pll.create_clkout(self.cd_init, 24e6) + pll.create_clkout(self.cd_clk10, 10e6) + self.specials += [ + Instance("ECLKBRIDGECS", + i_CLK0 = self.cd_sys2x_i.clk, + i_SEL = 0, + o_ECSOUT = sys2x_clk_ecsout), + Instance("ECLKSYNCB", + i_ECLKI = sys2x_clk_ecsout, + i_STOP = self.stop, + o_ECLKO = self.cd_sys2x.clk), + Instance("CLKDIVF", + p_DIV = "2.0", + i_ALIGNWD = 0, + i_CLKI = self.cd_sys2x.clk, + i_RST = self.cd_sys2x.rst, + o_CDIVX = self.cd_sys.clk), + AsyncResetSynchronizer(self.cd_init, ~por_done | ~pll.locked), + AsyncResetSynchronizer(self.cd_sys, ~por_done | ~pll.locked), + AsyncResetSynchronizer(self.cd_clk10, ~por_done | ~pll.locked) + ] + + # USB PLL + if with_usb_pll: + self.clock_domains.cd_usb_12 = ClockDomain() + self.clock_domains.cd_usb_48 = ClockDomain() + usb_pll = ECP5PLL() + self.submodules += usb_pll + usb_pll.register_clkin(clk25, 25e6) + usb_pll.create_clkout(self.cd_usb_48, 48e6) + usb_pll.create_clkout(self.cd_usb_12, 12e6) + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + def __init__(self, revision="rev0", device="45F", sdram_device="MT41K512M16", + with_ethernet=False, + sys_clk_freq=int(75e6), toolchain="trellis", **kwargs): + platform = logicbone.Platform(revision=revision, device=device ,toolchain=toolchain) + + # Serial ----------------------------------------------------------------------------------- + if kwargs["uart_name"] == "usb_acm": + # FIXME: do proper install of ValentyUSB. + os.system("git clone https://github.com/gregdavill/valentyusb -b hw_cdc_eptri") + sys.path.append("valentyusb") + + # SoCCore ---------------------------------------------------------------------------------- + SoCCore.__init__(self, platform, clk_freq=sys_clk_freq, **kwargs) + + # CRG -------------------------------------------------------------------------------------- + with_usb_pll = kwargs.get("uart_name", None) == "usb_acm" + self.submodules.crg = _CRG(platform, sys_clk_freq, with_usb_pll) + + # DDR3 SDRAM ------------------------------------------------------------------------------- + if not self.integrated_main_ram_size: + available_sdram_modules = { + "MT41K512M16": MT41K512M16, + #"AS4C1GM8": AS4C1GM8, ## Too many rows, seems to break things. + } + sdram_module = available_sdram_modules.get(sdram_device) + + self.submodules.ddrphy = ECP5DDRPHY( + platform.request("ddram"), + sys_clk_freq=sys_clk_freq) + self.add_csr("ddrphy") + self.comb += self.crg.stop.eq(self.ddrphy.init.stop) + self.add_sdram("sdram", + phy = self.ddrphy, + module = sdram_module(sys_clk_freq, "1:2"), + origin = self.mem_map["main_ram"], + size = kwargs.get("max_sdram_size", 0x40000000), + l2_cache_size = kwargs.get("l2_size", 8192), + l2_cache_min_data_width = kwargs.get("min_l2_data_width", 128), + l2_cache_reverse = True + ) + + # Ethernet --------------------------------------------------------------------------------- + if with_ethernet: + self.submodules.ethphy = LiteEthPHYRGMII( + clock_pads = self.platform.request("eth_clocks"), + pads = self.platform.request("eth")) + self.add_csr("ethphy") + self.add_ethernet(phy=self.ethphy) + + + # Leds ------------------------------------------------------------------------------------- + self.submodules.leds = LedChaser( + pads = Cat(*[platform.request("user_led", i) for i in range(4)]), + sys_clk_freq = sys_clk_freq) + self.add_csr("leds") + +# Build -------------------------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description="LiteX SoC on Logicbone") + parser.add_argument("--build", action="store_true", help="Build bitstream") + parser.add_argument("--load", action="store_true", help="Load bitstream") + parser.add_argument("--toolchain", default="trellis", help="Gateware toolchain to use, trellis (default) or diamond") + builder_args(parser) + soc_sdram_args(parser) + trellis_args(parser) + parser.add_argument("--sys-clk-freq", default=75e6, help="System clock frequency (default=75MHz)") + parser.add_argument("--device", default="45F", help="ECP5 device (default=45F)") + parser.add_argument("--sdram-device", default="MT41K512M16", help="ECP5 device (default=MT41K512M16)") + parser.add_argument("--with-ethernet", action="store_true", help="enable Ethernet support") + parser.add_argument("--with-sdcard", action="store_true", help="enable SDCard support") + args = parser.parse_args() + + soc = BaseSoC( + toolchain = args.toolchain, + revision = args.revision, + device = args.device, + sdram_device = args.sdram_device, + with_ethernet=args.with_ethernet, + sys_clk_freq = int(float(args.sys_clk_freq)), + **soc_sdram_argdict(args)) + if args.with_sdcard: + soc.add_sdcard() + builder = Builder(soc, **builder_argdict(args)) + builder_kargs = trellis_argdict(args) if args.toolchain == "trellis" else {} + builder.build(**builder_kargs, run=args.build) + + if args.load: + prog = soc.platform.create_programmer() + prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".bit")) + +if __name__ == "__main__": + main()