From 21d0c075a1f72ab8331dd8df61ce152e0a23a75c Mon Sep 17 00:00:00 2001 From: Raptor Engineering Development Team Date: Sat, 20 Mar 2021 17:58:30 -0500 Subject: [PATCH] Apply Kestrel changes to latest LiteX boards upstream tree --- litex_boards/platforms/versa_ecp5.py | 83 ++++++++ litex_boards/targets/versa_ecp5.py | 271 ++++++++++++++++++++++++++- 2 files changed, 344 insertions(+), 10 deletions(-) diff --git a/litex_boards/platforms/versa_ecp5.py b/litex_boards/platforms/versa_ecp5.py index f8dd49a..72d02dc 100644 --- a/litex_boards/platforms/versa_ecp5.py +++ b/litex_boards/platforms/versa_ecp5.py @@ -3,6 +3,7 @@ # # Copyright (c) 2017 Sergiusz Bazanski # Copyright (c) 2018-2019 Florent Kermarrec +# Copyright (c) 2020-2021 Raptor Engineering, LLC # SPDX-License-Identifier: BSD-2-Clause from litex.build.generic_platform import * @@ -25,6 +26,7 @@ _io = [ ("user_led", 5, Pins("F18"), IOStandard("LVCMOS25")), ("user_led", 6, Pins("E17"), IOStandard("LVCMOS25")), ("user_led", 7, Pins("F16"), IOStandard("LVCMOS25")), + ("user_leds", 0, Pins("E16 D17 D18 E18 F17 F18 E17 F16"), IOStandard("LVCMOS25")), # Switches ("user_dip_btn", 0, Pins("H2"), IOStandard("LVCMOS15")), @@ -36,6 +38,25 @@ _io = [ ("user_dip_btn", 6, Pins("K19"), IOStandard("LVCMOS25")), ("user_dip_btn", 7, Pins("K20"), IOStandard("LVCMOS25")), + # Alphanumeric display + ("alpha_led", 0, Pins("M20"), IOStandard("LVCMOS25")), + ("alpha_led", 1, Pins("L18"), IOStandard("LVCMOS25")), + ("alpha_led", 2, Pins("M19"), IOStandard("LVCMOS25")), + ("alpha_led", 3, Pins("L16"), IOStandard("LVCMOS25")), + ("alpha_led", 4, Pins("L17"), IOStandard("LVCMOS25")), + ("alpha_led", 5, Pins("M18"), IOStandard("LVCMOS25")), + ("alpha_led", 6, Pins("N16"), IOStandard("LVCMOS25")), + ("alpha_led", 7, Pins("M17"), IOStandard("LVCMOS25")), + ("alpha_led", 8, Pins("N18"), IOStandard("LVCMOS25")), + ("alpha_led", 9, Pins("P17"), IOStandard("LVCMOS25")), + ("alpha_led", 10, Pins("N17"), IOStandard("LVCMOS25")), + ("alpha_led", 11, Pins("P16"), IOStandard("LVCMOS25")), + ("alpha_led", 12, Pins("R16"), IOStandard("LVCMOS25")), + ("alpha_led", 13, Pins("R17"), IOStandard("LVCMOS25")), + ("alpha_led", 14, Pins("U1"), IOStandard("LVCMOS25")), + ("alpha_led", 15, Pins("T16"), IOStandard("LVCMOS25")), # Not wired on Versa board, but makes GPIO bank 16 bits. Future expansion? + ("alpha_leds", 0, Pins("M20 L18 M19 L16 L17 M18 N16 M17 N18 P17 N17 P16 R16 R17 U1 T16"), IOStandard("LVCMOS25")), # T16 not wired on Versa board, but makes GPIO bank 16 bits. Future expansion? + # Serial ("serial", 0, Subsignal("rx", Pins("C11"), IOStandard("LVCMOS33")), @@ -156,6 +177,68 @@ _io = [ Subsignal("p", Pins("Y7")), Subsignal("n", Pins("Y8")), ), + + ("i2c_bus1_master", 0, + Subsignal("sda", Pins("D12"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + Subsignal("scl", Pins("B10"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + ), + + ("i2c_bus2_master", 0, + Subsignal("sda", Pins("C10"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + Subsignal("scl", Pins("A9"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + ), + + ("i2c_bus3_master", 0, + Subsignal("sda", Pins("B17"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + Subsignal("scl", Pins("C17"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + ), + + ("i2c_bus4_master", 0, + Subsignal("sda", Pins("B18"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + Subsignal("scl", Pins("A18"), IOStandard("LVCMOS33"), Misc("PULLMODE=UP")), + ), + + ("openfsi_master", 0, + Subsignal("clock", Pins("B15"), IOStandard("LVCMOS33")), + Subsignal("data", Pins("C15"), IOStandard("LVCMOS33")), + Subsignal("data_direction", Pins("D15"), IOStandard("LVCMOS33")), + ), + + ("hostspiflash4x", 0, + Subsignal("cs_n", Pins("A14")), + Subsignal("clk", Pins("A12")), + Subsignal("dq", Pins("B13 D13 D11 D14")), + IOStandard("LVCMOS33"), + Misc("SLEWRATE=SLOW"), + Misc("DRIVE=16"), + ), + + ("hostlpcslave", 0, + Subsignal("frame_n", Pins("E15"), Misc("PULLMODE=UP")), + Subsignal("reset_n", Pins("B16"), Misc("PULLMODE=UP")), + Subsignal("pwrdn_n", Pins("A16"), Misc("PULLMODE=UP")), + Subsignal("clkrun_n", Pins("D16"), Misc("PULLMODE=UP")), + Subsignal("tpm_gpio0", Pins("A17"), Misc("PULLMODE=UP")), + Subsignal("addrdata", Pins("C14 E13 C13 A13"), Misc("PULLMODE=UP")), + Subsignal("serirq", Pins("E14"), Misc("PULLMODE=UP")), + Subsignal("clk", Pins("B12"), Misc("PULLMODE=NONE")), # Must be PCLK *not* GR_PCLK or (even worse) a general logic I/O! Pullup NONE helps minimize clock distortion / clock failure on heavily loaded clock lines... + IOStandard("LVCMOS33"), + Misc("SLEWRATE=SLOW"), + Misc("DRIVE=16"), + ), + + ("debug_port_2", 0, + Subsignal("led_15", Pins("B19"), IOStandard("LVCMOS33")), + Subsignal("led_14", Pins("B9"), IOStandard("LVCMOS33")), + Subsignal("led_13", Pins("D6"), IOStandard("LVCMOS33")), + Subsignal("led_12", Pins("D7"), IOStandard("LVCMOS33")), + Subsignal("led_11", Pins("B6"), IOStandard("LVCMOS33")), + Subsignal("led_10", Pins("D9"), IOStandard("LVCMOS33")), + Subsignal("led_9", Pins("C8"), IOStandard("LVCMOS33")), + Subsignal("led_8", Pins("E8"), IOStandard("LVCMOS33")), + ), + + ("lpc_debug_mirror_clock", 0, Pins("E12"), IOStandard("LVCMOS33")) ] # ECP5-hat extension (https://github.com/daveshah1/ecp5-hat) --------------------------------------- diff --git a/litex_boards/targets/versa_ecp5.py b/litex_boards/targets/versa_ecp5.py index 7526dbc..83ce813 100755 --- a/litex_boards/targets/versa_ecp5.py +++ b/litex_boards/targets/versa_ecp5.py @@ -1,14 +1,17 @@ #!/usr/bin/env python3 # -# This file is part of LiteX-Boards. +# This file is part of Kestrel # # Copyright (c) 2018-2019 Florent Kermarrec # Copyright (c) 2018-2019 David Shah +# Copyright (c) 2020-2021 Raptor Engineering, LLC # SPDX-License-Identifier: BSD-2-Clause import os import argparse +import subprocess +import tempfile from migen import * from migen.genlib.resetsync import AsyncResetSynchronizer @@ -18,6 +21,7 @@ from litex_boards.platforms import versa_ecp5 from litex.build.lattice.trellis import trellis_args, trellis_argdict from litex.soc.cores.clock import * +from litex.soc.integration.soc import SoCRegion from litex.soc.integration.soc_core import * from litex.soc.integration.soc_sdram import * from litex.soc.integration.builder import * @@ -28,6 +32,17 @@ from litedram.phy import ECP5DDRPHY from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII +# Kestrel-specific peripherals +from tercelspi.tercelspi import TercelSPI +from aquilalpc.aquilalpc import AquilaLPCSlave +from swiftfsi.swiftfsi import OpenFSIMaster +from simplertc.simplertc import SimpleRTCSlave +from opencoresi2c.opencoresi2c import OpenCoresI2CMaster + +# Useful constants +kB = 1024 +mB = 1024*kB + # CRG ---------------------------------------------------------------------------------------------- class _CRG(Module): @@ -79,16 +94,55 @@ class _CRG(Module): # BaseSoC ------------------------------------------------------------------------------------------ class BaseSoC(SoCCore): - def __init__(self, sys_clk_freq=int(75e6), device="LFE5UM5G", with_ethernet=False, with_etherbone=False, eth_ip="192.168.1.50", eth_phy=0, toolchain="trellis", **kwargs): + mem_map = { + "hostxicsicp" : 0xc3000000, + "hostxicsics" : 0xc3001000, + "ethmac" : 0xc3002000, + "hostspiflashcfg" : 0xc3007000, + "simplertc" : 0xc3008000, + "openfsimaster" : 0xc3009000, + "i2cmaster1" : 0xc300a000, + "i2cmaster2" : 0xc300a020, + "i2cmaster3" : 0xc300a040, + "i2cmaster4" : 0xc300a060, + "hostspiflash" : 0xc4000000, + "hostlpcslave" : 0xcb000000, + } + mem_map.update(SoCCore.mem_map) + + interrupt_map = { + "ethmac" : 2, + "hostlpcslave" : 3, + "openfsimaster" : 4, + "i2cmaster1" : 5, + "i2cmaster2" : 6, + "i2cmaster3" : 7, + "i2cmaster4" : 8, + } + interrupt_map.update(SoCCore.interrupt_map) + + def __init__(self, sys_clk_freq=int(50e6), device="LFE5UM5G", with_ethernet=False, with_etherbone=False, with_openfsi_master=True, with_hostspiflash=True, with_hostlpcslave=True, with_i2c_masters=True, with_simple_rtc=True, eth_ip="192.168.1.50", eth_phy=0, toolchain="trellis", **kwargs): platform = versa_ecp5.Platform(toolchain=toolchain, device=device) # FIXME: adapt integrated rom size for Microwatt if kwargs.get("cpu_type", None) == "microwatt": - kwargs["integrated_rom_size"] = 0xb000 if with_ethernet else 0x9000 + kwargs["integrated_rom_size"] = 0xd000 if with_ethernet else 0xb000 + else: + # There are numerous PowerPC-isms scattered throughout the HDL and firmware. + # Even if you get a bistream out with a non-PowerPC CPU, it probably won't + # work, and the firmware almost certainly won't build, let alone function. + # + # If you are an end user or software developer, you probably forgot to pass + # "--cpu-type=microwatt" to the build script. + # + # If you are a developer and are trying to port the Kestrel HDL and firmware + # to a non-PowerPC CPU, you probably know what you're doing and can debug + # whatever you break on your own hardware. Remove this line and keep hacking! + raise OSError("Kestrel HDL and firmware currently require a PowerPC-compatible CPU to function. Did you forget '--cpu-type=microwatt'?") # SoCCore -----------------------------------------_---------------------------------------- - SoCCore.__init__(self, platform, sys_clk_freq, - ident = "LiteX SoC on Versa ECP5", + SoCCore.__init__(self, platform, csr_data_width=32, irq_n_irqs=16, clk_freq=sys_clk_freq, + ident = "Kestrel SoC on Versa ECP5", ident_version = True, **kwargs) @@ -126,11 +180,187 @@ class BaseSoC(SoCCore): if with_etherbone: self.add_etherbone(phy=self.ethphy, ip_address=eth_ip) - # Leds ------------------------------------------------------------------------------------- - self.submodules.leds = LedChaser( - pads = platform.request_all("user_led"), - sys_clk_freq = sys_clk_freq) - self.add_csr("leds") + # Debug pad locator + debug2_pads = platform.request("debug_port_2") + lpc_debug_mirror_clock_pad = platform.request("lpc_debug_mirror_clock") + + # Host SPI Flash (Tercel core) ------------------------------------------------------------- + if with_hostspiflash: + hostspiflash4x_pads = platform.request("hostspiflash4x") + self.host_spi_dq_debug = [Signal(), Signal(), Signal(), Signal(), Signal(), Signal()] + self.submodules.hostspiflash = TercelSPI( + platform = platform, + pads = hostspiflash4x_pads, + debug_signals = self.host_spi_dq_debug, + clk_freq = sys_clk_freq, + endianness = self.cpu.endianness, + adr_offset = self.mem_map.get("hostspiflash", None)) + self.add_csr("hostspiflash") + hostspiflash_size = 64*mB + hostspiflash_region = SoCRegion(origin=self.mem_map.get("hostspiflash", None), size=hostspiflash_size, cached=False) + self.bus.add_slave(name="hostspiflash", slave=self.hostspiflash.bus, region=hostspiflash_region) + hostspiflashcfg_size = 128 + hostspiflashcfg_region = SoCRegion(origin=self.mem_map.get("hostspiflashcfg", None), size=hostspiflashcfg_size, cached=False) + self.bus.add_slave(name="hostspiflashcfg", slave=self.hostspiflash.cfg_bus, region=hostspiflashcfg_region) + + # Host LPC Slave (Aquila core) ------------------------------------------------------------- + if with_hostlpcslave: + hostlpcslave_pads = platform.request("hostlpcslave") + self.host_lpc_debug = [Signal(), Signal(), Signal(), Signal(), Signal(), Signal(), Signal(), Signal()] + self.host_lpc_clock_mirror = Signal() + self.submodules.hostlpcslave = AquilaLPCSlave( + platform = platform, + pads = hostlpcslave_pads, + debug_signals = self.host_lpc_debug, + lpc_clk_mirror = self.host_lpc_clock_mirror, + endianness = self.cpu.endianness, + adr_offset = self.mem_map.get("hostlpcslave", None)) + self.add_csr("hostlpcslave") + hostlpcslave_size = 16*mB + hostlpcslave_region = SoCRegion(origin=self.mem_map.get("hostlpcslave", None), size=hostlpcslave_size, cached=False) + self.bus.add_slave(name="hostlpcslave", slave=self.hostlpcslave.slave_bus, region=hostlpcslave_region) + self.bus.add_master(name="hostlpcslave", master=self.hostlpcslave.master_bus) + + # OpenFSI Master --------------------------------------------------------------------------- + if with_openfsi_master: + openfsi_master_pads = platform.request("openfsi_master") + self.submodules.openfsi_master = OpenFSIMaster( + platform = platform, + pads = openfsi_master_pads, + endianness = self.cpu.endianness) + self.add_csr("openfsimaster") + openfsi_master_size = 128 + openfsi_master_region = SoCRegion(origin=self.mem_map.get("openfsimaster", None), size=openfsi_master_size, cached=False) + self.bus.add_slave(name="openfsimaster", slave=self.openfsi_master.slave_bus, region=openfsi_master_region) + + # Debug hookups... + #self.comb += debug2_pads.led_15.eq(self.host_spi_dq_debug[5]) + #self.comb += debug2_pads.led_14.eq(self.host_spi_dq_debug[4]) + #self.comb += debug2_pads.led_13.eq(hostspiflash4x_pads.clk) + #self.comb += debug2_pads.led_12.eq(hostspiflash4x_pads.cs_n) + #self.comb += debug2_pads.led_11.eq(self.host_spi_dq_debug[3]) + #self.comb += debug2_pads.led_10.eq(self.host_spi_dq_debug[2]) + #self.comb += debug2_pads.led_9.eq(self.host_spi_dq_debug[1]) + #self.comb += debug2_pads.led_8.eq(self.host_spi_dq_debug[0]) + + if with_hostlpcslave: + self.comb += debug2_pads.led_15.eq(self.host_lpc_debug[7]) + self.comb += debug2_pads.led_14.eq(self.host_lpc_debug[6]) + self.comb += debug2_pads.led_13.eq(self.host_lpc_debug[5]) + self.comb += debug2_pads.led_12.eq(self.host_lpc_debug[4]) + self.comb += debug2_pads.led_11.eq(self.host_lpc_debug[3]) + self.comb += debug2_pads.led_10.eq(self.host_lpc_debug[2]) + self.comb += debug2_pads.led_9.eq(self.host_lpc_debug[1]) + self.comb += debug2_pads.led_8.eq(self.host_lpc_debug[0]) + self.comb += lpc_debug_mirror_clock_pad.eq(self.host_lpc_clock_mirror) + + # I2C Masters ------------------------------------------------------------------------------ + if with_i2c_masters: + i2cmaster1_pads = platform.request("i2c_bus1_master") + self.submodules.i2cmaster1 = OpenCoresI2CMaster( + platform = platform, + pads = i2cmaster1_pads, + clk_freq = sys_clk_freq) + self.add_csr("i2cmaster1") + i2cmaster1_size = 32 + i2cmaster1_region = SoCRegion(origin=self.mem_map.get("i2cmaster1", None), size=i2cmaster1_size, cached=False) + self.bus.add_slave(name="i2cmaster1", slave=self.i2cmaster1.bus, region=i2cmaster1_region) + + if with_i2c_masters: + i2cmaster2_pads = platform.request("i2c_bus2_master") + self.submodules.i2cmaster2 = OpenCoresI2CMaster( + platform = platform, + pads = i2cmaster2_pads, + clk_freq = sys_clk_freq) + self.add_csr("i2cmaster2") + i2cmaster2_size = 32 + i2cmaster2_region = SoCRegion(origin=self.mem_map.get("i2cmaster2", None), size=i2cmaster2_size, cached=False) + self.bus.add_slave(name="i2cmaster2", slave=self.i2cmaster2.bus, region=i2cmaster2_region) + + # Not needed for Blackbird / Talos II / Sparrowhawk; save space on the Versa board ECP5 -45 device + #if with_i2c_masters: + #i2cmaster3_pads = platform.request("i2c_bus3_master") + #self.submodules.i2cmaster3 = OpenCoresI2CMaster( + #platform = platform, + #pads = i2cmaster3_pads, + #clk_freq = sys_clk_freq) + #self.add_csr("i2cmaster3") + #i2cmaster3_size = 32 + #i2cmaster3_region = SoCRegion(origin=self.mem_map.get("i2cmaster3", None), size=i2cmaster3_size, cached=False) + #self.bus.add_slave(name="i2cmaster3", slave=self.i2cmaster3.bus, region=i2cmaster3_region) + + if with_i2c_masters: + i2cmaster4_pads = platform.request("i2c_bus4_master") + self.submodules.i2cmaster4 = OpenCoresI2CMaster( + platform = platform, + pads = i2cmaster4_pads, + clk_freq = sys_clk_freq) + self.add_csr("i2cmaster4") + i2cmaster4_size = 32 + i2cmaster4_region = SoCRegion(origin=self.mem_map.get("i2cmaster4", None), size=i2cmaster4_size, cached=False) + self.bus.add_slave(name="i2cmaster4", slave=self.i2cmaster4.bus, region=i2cmaster4_region) + + # SimpleRTC -------------------------------------------------------------------------------- + if with_simple_rtc: + self.submodules.simple_rtc = SimpleRTCSlave( + platform = platform, + endianness = self.cpu.endianness, + rtc_clk_src = 'sys', + rtc_clk_freq = sys_clk_freq) + self.add_csr("simplertc") + simple_rtc_size = 128 + simple_rtc_region = SoCRegion(origin=self.mem_map.get("simplertc", None), size=simple_rtc_size, cached=False) + self.bus.add_slave(name="simplertc", slave=self.simple_rtc.slave_bus, region=simple_rtc_region) + + # Discrete LEDs ---------------------------------------------------------------------------- + from litex.soc.cores.gpio import GPIOTristate + + self.submodules.gpio1 = GPIOTristate( + pads = platform.request("user_leds")) + self.add_csr("gpio1") + + # DIP switches ------------------------------------------------------------------------------------- + from litex.soc.cores.gpio import GPIOIn + + self.submodules.gpio2 = GPIOIn( + pads = Cat(*[platform.request("user_dip_btn", i) for i in range(8)])) + self.add_csr("gpio2") + + # Alphanumeric display ----------------------------------------------------------------------------- + from litex.soc.cores.gpio import GPIOTristate + + self.submodules.gpio3 = GPIOTristate( + pads = platform.request("alpha_leds")) + self.add_csr("gpio3") + + def set_gateware_dir(self, gateware_dir): + self.gateware_dir = gateware_dir + + def initialize_rom(self, data): + # Save actual expected contents for future use as gateware/rom.init + content = "" + formatter = "{:0" + str(int(self.rom.mem.width / 4)) + "X}\n" + for d in data: + content += formatter.format(d).zfill(int(self.rom.mem.width / 4)) + romfile = os.open(os.path.join(self.gateware_dir, "rom_data.init"), os.O_WRONLY | os.O_CREAT) + os.write(romfile, content.encode()) + os.close(romfile) + + # Generate initial data to allow ecpbram to later stuff the bitstream + (_, path) = tempfile.mkstemp() + subprocess.check_call(["ecpbram", "-g", path, "-w", str(self.rom.mem.width), "-d", str(int(self.integrated_rom_size / 4)), "-s" "0"]) + + # Convert data to binary + random_file = open(path, 'r') + data = [] + random_lines = random_file.readlines() + for line in random_lines: + data.append(int(line, 16)) + + os.remove(path) + + self.rom.mem.init = data + self.rom.mem.name_override = "rom" # Build -------------------------------------------------------------------------------------------- @@ -156,15 +386,36 @@ def main(): device = args.device, with_ethernet = args.with_ethernet, with_etherbone = args.with_etherbone, + with_hostspiflash = True, + with_hostlpcslave = True, + with_openfsi_master = True, + with_i2c_masters = True, eth_ip = args.eth_ip, eth_phy = args.eth_phy, toolchain = args.toolchain, **soc_sdram_argdict(args) ) builder = Builder(soc, **builder_argdict(args)) + + soc.set_gateware_dir(builder.gateware_dir) + builder_kargs = trellis_argdict(args) if args.toolchain == "trellis" else {} builder.build(**builder_kargs, run=args.build) + # Stuff the original rom into the fpga + subprocess.check_call(["ecpbram", + "-i", os.path.join(builder.gateware_dir, soc.platform.name + ".config"), + "-o", os.path.join(builder.gateware_dir, soc.platform.name + "_stuffed.config"), + "-f", os.path.join(builder.gateware_dir, "rom.init"), + "-t", os.path.join(builder.gateware_dir, "rom_data.init")]) + + # Update the svf / bit files + subprocess.check_call(["ecppack", + os.path.join(builder.gateware_dir, soc.platform.name + "_stuffed.config"), + "--svf", os.path.join(builder.gateware_dir, soc.platform.name + ".svf"), + "--bit", os.path.join(builder.gateware_dir, soc.platform.name + ".bit"), + "--bootaddr", "0"]) + if args.load: prog = soc.platform.create_programmer() prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".svf")) -- 2.30.2