Add Logicbone ECP5 board

The Logicbone is an Open Source development board for the Lattice ECP5
being developed at https://github.com/oskirby/logicbone
This commit is contained in:
Owen Kirby 2020-06-27 03:26:44 -07:00
parent 0ecb8609b3
commit 76a32ba8ec
2 changed files with 381 additions and 0 deletions

View File

@ -0,0 +1,195 @@
# This file is Copyright (c) 2020 Owen Kirby <oskirby@gmail.com>
# 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)

View File

@ -0,0 +1,186 @@
#!/usr/bin/env python3
# This file is Copyright (c) 2020 Owen Kirby <oskirby@gmail.com>
# 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()