add the Hackaday Supercon ECP5 badge

Add the Hackaday Supercon 2019 badge which has an ECP5 FPGA:
https://hackaday.io/project/167255-2019-hackaday-superconference-badge

These changes are from Michael Welling's fork:
https://github.com/mwelling/linux-on-litex-vexriscv

During Supercon, we trying two approaches:
- use the built-in 16MB QSPI SRAM
- use add-on cartiridge with 32MB SDRAM by Jacob Creedon

We were not able to get the QSPI SRAM working so I've removed
those changes, and I have just added the changes that are needed
to boot Linux with the 32MB SDRAM.

Thanks to Jacob Creedon, Greg Davill and Tim Ansell who helped debug.

KiCad design files for the SDRAM cartridge are available at:
https://github.com/jcreedon/dram-cart/

The SDRAM cartridge PCB is shared at:
https://oshpark.com/shared_projects/IQSl2lid

More information in this blog post:
https://blog.oshpark.com/2019/12/20/

The Hackaday Supercon badge PCB design is here:
https://github.com/Spritetm/hadbadge2019_pcb
This commit is contained in:
Drew Fustini 2020-01-06 00:46:13 +01:00
parent f8f2301a3e
commit b3f175c064
2 changed files with 461 additions and 0 deletions

View File

@ -0,0 +1,215 @@
from litex.build.generic_platform import *
from litex.build.lattice import LatticePlatform
# IOs ----------------------------------------------------------------------------------------------
_io = [
("clk8", 0, Pins("U18"), IOStandard("LVCMOS33")),
("programn", 0, Pins("R1"), IOStandard("LVCMOS33")),
("serial", 0,
Subsignal("rx", Pins("U2"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")),
Subsignal("tx", Pins("U1"), IOStandard("LVCMOS33")),
),
("led", 0, Pins("E3 D3 C3 C4 C2 B1 B20 B19 A18 K20 K19"), IOStandard("LVCMOS33")), # Anodes
("led", 1, Pins("P19 L18 K18"), IOStandard("LVCMOS33")), # Cathodes via FET
("usb", 0,
Subsignal("d_p", Pins("F3")),
Subsignal("d_n", Pins("G3")),
Subsignal("pullup", Pins("E4")),
Subsignal("vbusdet", Pins("F4")),
IOStandard("LVCMOS33")
),
("keypad", 0,
Subsignal("left", Pins("G2"), Misc("PULLMODE=UP")),
Subsignal("right", Pins("F2"), Misc("PULLMODE=UP")),
Subsignal("up", Pins("F1"), Misc("PULLMODE=UP")),
Subsignal("down", Pins("C1"), Misc("PULLMODE=UP")),
Subsignal("start", Pins("E1"), Misc("PULLMODE=UP")),
Subsignal("select", Pins("D2"), Misc("PULLMODE=UP")),
Subsignal("a", Pins("D1"), Misc("PULLMODE=UP")),
Subsignal("b", Pins("E2"), Misc("PULLMODE=UP")),
),
("hdmi_out", 0,
Subsignal("clk_p", Pins("P20"), Inverted(), IOStandard("TMDS_33")),
Subsignal("clk_n", Pins("R20"), Inverted(), IOStandard("TMDS_33")),
Subsignal("data0_p", Pins("N19"), IOStandard("TMDS_33")),
Subsignal("data0_n", Pins("N20"), IOStandard("TMDS_33")),
Subsignal("data1_p", Pins("L20"), IOStandard("TMDS_33")),
Subsignal("data1_n", Pins("M20"), IOStandard("TMDS_33")),
Subsignal("data2_p", Pins("L16"), IOStandard("TMDS_33")),
Subsignal("data2_n", Pins("L17"), IOStandard("TMDS_33")),
Subsignal("hpd_notif", Pins("R18"), IOStandard("LVCMOS33"), Inverted()), # Also called HDMI_HEAC_n
Subsignal("hdmi_heac_p", Pins("T19"), IOStandard("LVCMOS33"), Inverted()),
Misc("DRIVE=4"),
),
("lcd", 0,
Subsignal("db", Pins("J3 H1 K4 J1 K3 K2 L4 K1 L3 L2 M4 L1 M3 M1 N4 N2 N3 N1"), IOStandard("LVCMOS33")),
Subsignal("rd", Pins("P2"), IOStandard("LVCMOS33")),
Subsignal("wr", Pins("P4"), IOStandard("LVCMOS33")),
Subsignal("rs", Pins("P1"), IOStandard("LVCMOS33")),
Subsignal("cs", Pins("P3"), IOStandard("LVCMOS33")),
Subsignal("id", Pins("J4"), IOStandard("LVCMOS33")),
Subsignal("rst", Pins("H2"), IOStandard("LVCMOS33")),
Subsignal("fmark", Pins("G1"), IOStandard("LVCMOS33")),
Subsignal("blen", Pins("P5"), IOStandard("LVCMOS33")),
),
("spiflash", 0, # clock needs to be accessed through USRMCLK
Subsignal("cs_n", Pins("R2"), IOStandard("LVCMOS33")),
Subsignal("mosi", Pins("W2"), IOStandard("LVCMOS33")),
Subsignal("miso", Pins("V2"), IOStandard("LVCMOS33")),
Subsignal("wp", Pins("Y2"), IOStandard("LVCMOS33")),
Subsignal("hold", Pins("W1"), IOStandard("LVCMOS33")),
),
("spiflash4x", 0, # clock needs to be accessed through USRMCLK
Subsignal("cs_n", Pins("R2"), IOStandard("LVCMOS33")),
Subsignal("dq", Pins("W2 V2 Y2 W1"), IOStandard("LVCMOS33")),
),
("spiram4x", 0,
Subsignal("cs_n", Pins("D20"), IOStandard("LVCMOS33"), Misc("SLEWRATE=SLOW")),
Subsignal("clk", Pins("E20"), IOStandard("LVCMOS33"), Misc("SLEWRATE=SLOW")),
Subsignal("dq", Pins("E19 D19 C20 F19"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP"), Misc("SLEWRATE=SLOW")),
),
("spiram4x", 1,
Subsignal("cs_n", Pins("F20"), IOStandard("LVCMOS33"), Misc("SLEWRATE=SLOW")),
Subsignal("clk", Pins("J19"), IOStandard("LVCMOS33"), Misc("SLEWRATE=SLOW")),
Subsignal("dq", Pins("J20 G19 G20 H20"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP"), Misc("SLEWRATE=SLOW")),
),
("sao", 0,
Subsignal("sda", Pins("B3")),
Subsignal("scl", Pins("B2")),
Subsignal("gpio", Pins("A2 A3 B4")),
Subsignal("drm", Pins("A4")),
),
("sao", 1,
Subsignal("sda", Pins("A16")),
Subsignal("scl", Pins("B17")),
Subsignal("gpio", Pins("B18 A17 B16")),
Subsignal("drm", Pins("C17")),
),
("testpts", 0,
Subsignal("a1", Pins("A15")),
Subsignal("a2", Pins("C16")),
Subsignal("a3", Pins("A14")),
Subsignal("a4", Pins("D16")),
Subsignal("b1", Pins("B15")),
Subsignal("b2", Pins("C15")),
Subsignal("b3", Pins("A13")),
Subsignal("b4", Pins("B13")),
),
# rev a
# ("sdram_clock", 0, Pins("C11"), IOStandard("LVCMOS33")),
# ("sdram", 0,
# Subsignal("a", Pins("D10 C10 B10 A10 C14 E17 A12 B12 H17 G18 A9 A11 A7")),
# Subsignal("dq", Pins("C5 A5 B6 D6 B5 C6 A6 C7")),
# Subsignal("we_n", Pins("C8")),
# Subsignal("ras_n", Pins("A8")),
# Subsignal("cas_n", Pins("B8")),
# Subsignal("cs_n", Pins("D9")),
# Subsignal("cke", Pins("B11")),
# Subsignal("ba", Pins("C9 B9")),
# Subsignal("dm", Pins("D11")),
# IOStandard("LVCMOS33"), Misc("SLEWRATE=FAST")
# ),
# rev b
("sdram_clock", 0, Pins("D11"), IOStandard("LVCMOS33")),
("sdram", 0,
Subsignal("a", Pins("A8 D9 C9 B9 C14 E17 A12 B12 H17 G18 B8 A11 B11")),
Subsignal("dq", Pins("C5 B5 A5 C6 B10 C10 D10 A9")),
Subsignal("we_n", Pins("B6")),
Subsignal("ras_n", Pins("D6")),
Subsignal("cas_n", Pins("A6")),
Subsignal("cs_n", Pins("C7")),
Subsignal("cke", Pins("C11")),
Subsignal("ba", Pins("A7 C8")),
Subsignal("dm", Pins("A10")),
IOStandard("LVCMOS33"), Misc("SLEWRATE=FAST")
),
# Only used for simulation
("wishbone", 0,
Subsignal("adr", Pins(30)),
Subsignal("dat_r", Pins(32)),
Subsignal("dat_w", Pins(32)),
Subsignal("sel", Pins(4)),
Subsignal("cyc", Pins(1)),
Subsignal("stb", Pins(1)),
Subsignal("ack", Pins(1)),
Subsignal("we", Pins(1)),
Subsignal("cti", Pins(3)),
Subsignal("bte", Pins(2)),
Subsignal("err", Pins(1))
),
("reset", 0, Pins(1), IOStandard("LVCMOS33")),
]
_connectors = [
("pmod", "A15 C16 A14 D16 B15 C15 A13 B13"),
("genio", "C5 B5 A5 C6 B6 A6 D6 C7 A7 C8 B8 A8 D9 C9 B9 A9 D10 C10 B10 A10 D11 C11 B11 A11 G18 H17 B12 A12 E17 C14"),
]
_pmod_gpio = [
("pmod_gpio", 0,
Subsignal("p0", Pins("pmod:0")),
Subsignal("p1", Pins("pmod:1")),
Subsignal("p2", Pins("pmod:2")),
Subsignal("p3", Pins("pmod:3")),
Subsignal("p4", Pins("pmod:4")),
Subsignal("p5", Pins("pmod:5")),
Subsignal("p6", Pins("pmod:6")),
Subsignal("p7", Pins("pmod:7")),
IOStandard("LVCMOS33")
),
]
_genio_gpio = [
("genio_gpio", 0,
Subsignal("p0", Pins("genio:0")),
Subsignal("p1", Pins("genio:1")),
Subsignal("p2", Pins("genio:2")),
Subsignal("p3", Pins("genio:3")),
Subsignal("p4", Pins("genio:4")),
Subsignal("p5", Pins("genio:5")),
Subsignal("p6", Pins("genio:6")),
Subsignal("p7", Pins("genio:7")),
Subsignal("p8", Pins("genio:8")),
Subsignal("p9", Pins("genio:9")),
Subsignal("p10", Pins("genio:10")),
Subsignal("p11", Pins("genio:11")),
Subsignal("p12", Pins("genio:12")),
Subsignal("p13", Pins("genio:13")),
Subsignal("p14", Pins("genio:14")),
Subsignal("p15", Pins("genio:15")),
Subsignal("p16", Pins("genio:16")),
Subsignal("p17", Pins("genio:17")),
Subsignal("p18", Pins("genio:18")),
Subsignal("p19", Pins("genio:19")),
Subsignal("p20", Pins("genio:20")),
Subsignal("p21", Pins("genio:21")),
Subsignal("p22", Pins("genio:22")),
Subsignal("p23", Pins("genio:23")),
Subsignal("p24", Pins("genio:24")),
Subsignal("p25", Pins("genio:25")),
Subsignal("p26", Pins("genio:26")),
Subsignal("p27", Pins("genio:27")),
Subsignal("p28", Pins("genio:28")),
Subsignal("p29", Pins("genio:29")),
)
]
# Platform -----------------------------------------------------------------------------------------
class Platform(LatticePlatform):
default_clk_name = "clk8"
default_clk_period = 1e9/8e6
def __init__(self, device="LFE5U-45F", **kwargs):
LatticePlatform.__init__(self, device + "-CABGA381", io=_io, connectors=_connectors, toolchain="trellis", **kwargs)
def create_programmer(self):
raise ValueError("{} programmer is not supported"
.format(self.programmer))
def do_finalize(self, fragment):
LatticePlatform.do_finalize(self, fragment)

View File

@ -0,0 +1,246 @@
#!/usr/bin/env python3
# This file is Copyright (c) 2018-2019 Florent Kermarrec <florent@enjoy-digital.fr>
# This file is Copyright (c) 2018 David Shah <dave@ds0.me>
# License: BSD
import argparse
import sys
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex_boards.platforms import hadbadge
from litex.soc.cores.clock import *
from litex.soc.integration.soc_sdram import *
#from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
#from .spi_ram_dual import SpiRamDualQuad
from litedram import modules as litedram_modules
from litedram.phy import GENSDRPHY
# CRG ----------------------------------------------------------------------------------------------
class _CRG(Module):
"""Clock Resource Generator"
Input: 8 MHz
Output: 48 MHz
"""
def __init__(self, platform, sys_clk_freq):
self.clock_domains.cd_por = ClockDomain(reset_less=True)
self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_clk12 = ClockDomain()
self.clock_domains.cd_clk48 = ClockDomain()
self.clock_domains.cd_sys_ps = ClockDomain(reset_less=True)
# # #
self.cd_sys.clk.attr.add("keep")
self.cd_por.clk.attr.add("keep")
self.cd_clk12.clk.attr.add("keep")
self.cd_clk48.clk.attr.add("keep")
self.cd_sys_ps.clk.attr.add("keep")
self.stop = Signal()
# clk / rst
clk8 = platform.request("clk8")
# rst_n = platform.request("rst_n")
platform.add_period_constraint(clk8, 1e9/8e6)
platform.add_period_constraint(self.cd_sys.clk, 1e9/48e6)
platform.add_period_constraint(self.cd_clk12.clk, 1e9/12e6)
platform.add_period_constraint(self.cd_clk48.clk, 1e9/48e6)
# power on reset
por_count = Signal(16, reset=2**16-1)
por_done = Signal()
self.comb += self.cd_por.clk.eq(clk8)
self.comb += por_done.eq(por_count == 0)
self.sync.por += If(~por_done, por_count.eq(por_count - 1))
# pll
self.submodules.pll = pll = ECP5PLL()
pll.register_clkin(clk8, 8e6)
pll.create_clkout(self.cd_sys, sys_clk_freq, phase=0, margin=1e-9)
pll.create_clkout(self.cd_clk12, 12e6, phase=39, margin=1e-9)
pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=20)
self.comb += self.cd_clk48.clk.eq(self.cd_sys.clk)
# pll.create_clkout(self.cd_sys, 48e6, phase=0, margin=1e-9)
# pll.create_clkout(self.cd_clk12, 12e6, phase=132, margin=1e-9)
# sdram clock
self.comb += platform.request("sdram_clock").eq(self.cd_sys_ps.clk)
# Synchronize USB48 and USB12, and drive USB12 from USB48
self.specials += [
# Instance("ECLKSYNCB",
# i_ECLKI=self.cd_usb48_i.clk,
# i_STOP=self.stop,
# o_ECLKO=self.cd_usb48.clk),
# Instance("CLKDIVF",
# p_DIV="2.0",
# i_ALIGNWD=0,
# i_CLKI=self.cd_usb48.clk,
# i_RST=self.cd_usb48.rst,
# o_CDIVX=self.cd_usb12.clk),
AsyncResetSynchronizer(self.cd_sys, ~por_done | ~pll.locked),# | ~rst_n),
AsyncResetSynchronizer(self.cd_clk12, ~por_done | ~pll.locked),# | ~rst_n),
# AsyncResetSynchronizer(self.cd_usb48, ~por_done | ~pll.locked),# | ~rst_n)
]
# BaseSoC ------------------------------------------------------------------------------------------
class BaseSoC(SoCSDRAM):
# SoCCore.csr_map = {
# "ctrl": 0, # provided by default (optional)
# "crg": 1, # user
# "uart_phy": 2, # provided by default (optional)
# "uart": 3, # provided by default (optional)
# "identifier_mem": 4, # provided by default (optional)
# "timer0": 5, # provided by default (optional)
# "picorvspi": 7,
# "lcdif": 8,
# "usb": 9,
# "reboot": 12,
# "rgb": 13,
# "version": 14,
# "lxspi": 15,
# "messible": 16,
# }
# We must define memory offsets here rather than using the litex
# defaults. This is because the mmu only allows for certain
# regions to be unbuffered:
# https://github.com/m-labs/VexRiscv-verilog/blob/master/src/main/scala/vexriscv/GenCoreDefault.scala#L139-L143
SoCSDRAM.mem_map = {
"rom": 0x00000000,
"sram": 0x10000000,
"emulator_ram": 0x20000000,
"ethmac": 0x30000000,
"spiflash": 0x50000000,
"main_ram": 0xc0000000,
"csr": 0xe0000000,
}
def __init__(self, debug=True, sdram_module_cls="AS4C32M8", **kwargs):
platform = hadbadge.Platform()
clk_freq = int(48e6)
SoCSDRAM.__init__(self, platform, clk_freq,
integrated_rom_size=16384,
integrated_sram_size=65536,
wishbone_timeout_cycles=1e9,
**kwargs)
self.submodules.crg = _CRG(self.platform, sys_clk_freq=clk_freq)
# Add a "Version" module so we can see what version of the board this is.
# self.submodules.version = Version("proto2", [
# (0x02, "proto2", "Prototype Version 2 (red)")
# ], 0)
# Add a "USB" module to let us debug the device.
# usb_pads = platform.request("usb")
# usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
# self.submodules.usb = ClockDomainsRenamer({
# "usb_48": "clk48",
# "usb_12": "clk12",
# })(DummyUsb(usb_iobuf, debug=debug, product="Hackaday Supercon Badge", cdc=True))
#
# if debug:
# self.add_wb_master(self.usb.debug_bridge.wishbone)
#
# if self.cpu_type is not None:
# self.register_mem("vexriscv_debug", 0xb00f0000, self.cpu.debug_bus, 0x200)
# self.cpu.use_external_variant("rtl/VexRiscv_HaD_Debug.v")
# elif self.cpu_type is not None:
# self.cpu.use_external_variant("rtl/VexRiscv_HaD.v")
# Add the 16 MB SPI flash as XIP memory at address 0x03000000
# if not is_sim:
# # flash = SpiFlashDualQuad(platform.request("spiflash4x"), dummy=5)
# # flash.add_clk_primitive(self.platform.device)
# # self.submodules.lxspi = flash
# # self.register_mem("spiflash", 0x03000000, self.lxspi.bus, size=16 * 1024 * 1024)
# self.submodules.picorvspi = flash = PicoRVSpi(self.platform, pads=platform.request("spiflash"), size=16 * 1024 * 1024)
# self.register_mem("spiflash", self.mem_map["spiflash"], self.picorvspi.bus, size=self.picorvspi.size)
# # Add the 16 MB SPI RAM at address 0x40000000 # Value at 40010000: afbfcfef
# reset_cycles = 2**14-1
# ram = SpiRamDualQuad(platform.request("spiram4x", 0), platform.request("spiram4x", 1), dummy=5, reset_cycles=reset_cycles, qpi=True)
# self.submodules.ram = ram
# self.register_mem("main_ram", self.mem_map["main_ram"], self.ram.bus, size=16 * 1024 * 1024)
self.submodules.sdrphy = GENSDRPHY(platform.request("sdram"), cl=2)
sdram_module = getattr(litedram_modules, sdram_module_cls)(clk_freq, "1:1")
self.register_sdram(self.sdrphy,
sdram_module.geom_settings,
sdram_module.timing_settings)
# Let us reboot the device
# self.submodules.reboot = Reboot(platform.request("programn"))
# Add a Messible for sending messages to the host
# self.submodules.messible = Messible()
# Add an LCD so we can see what's up
# self.submodules.lcdif = LCDIF(platform.request("lcd"))
# Ensure timing is correctly set up
self.platform.toolchain.build_template[1] += " --speed 8" # Add "speed grade 8" to nextpnr-ecp5
# SAO
# self.submodules.sao0 = ShittyAddOn(self.platform, self.platform.request("sao", 0), disable_i2c=kwargs["sao0_disable_i2c"]);
# self.add_csr("sao0")
# self.submodules.sao1 = ShittyAddOn(self.platform, self.platform.request("sao", 1), disable_i2c=kwargs["sao1_disable_i2c"]);
# self.add_csr("sao1")
# # PMOD
# platform.add_extension(_pmod_gpio)
# self.submodules.pmod = GPIOBidirectional(self.platform.request("pmod_gpio"))
# self.add_csr("pmod")
# # GENIO
# platform.add_extension(_genio_gpio)
# self.submodules.genio = GPIOBidirectional(self.platform.request("genio_gpio"))
# self.add_csr("genio")
# # LEDs
# self.submodules.led0 = gpio.GPIOOut(self.platform.request("led", 0))
# self.add_csr("led0")
# self.submodules.led1 = gpio.GPIOOut(self.platform.request("led", 1))
# self.add_csr("led1")
# # Keypad
# self.submodules.keypad = gpio.GPIOIn(Cat(self.platform.request("keypad", 0).flatten()))
# self.add_csr("keypad")
# Build --------------------------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="LiteX SoC on Hackaday Badge")
parser.add_argument("--gateware-toolchain", dest="toolchain", default="trellis",
help='gateware toolchain to use, diamond or trellis (default)')
parser.add_argument("--device", dest="device", default="LFE5U-45F",
help='FPGA device, Hackaday badge is populated with LFE5U-45F')
parser.add_argument("--sys-clk-freq", default=48e6,
help="system clock frequency (default=48MHz)")
parser.add_argument("--sdram-module", default="MT48LC16M16",
help="SDRAM module: MT48LC16M16, AS4C32M16 or AS4C16M16 (default=MT48LC16M16)")
builder_args(parser)
soc_sdram_args(parser)
soc_core_args(parser)
args = parser.parse_args()
# soc = BaseSoC(device=args.device,
# sys_clk_freq=int(float(args.sys_clk_freq)),
# **soc_core_argdict(args))
soc = BaseSoC(device=args.device, toolchain=args.toolchain,
sys_clk_freq=int(float(args.sys_clk_freq)),
sdram_module_cls=args.sdram_module,
**soc_sdram_argdict(args))
builder = Builder(soc, **builder_argdict(args))
builder.build()
if __name__ == "__main__":
main()