gateware: move back to LiteX

Had too many issues with integrating LiteEth. I put my FW into a LiteX
SoC and it worked, so I migrated back. With the knowledge I gained doing
Amaranth I could fix the issues I had adding a wishbone slave device
pretty easily.
This commit is contained in:
David Lenfesty 2023-04-22 18:35:13 -06:00
parent 833db12d58
commit c0b293e0c7
3 changed files with 460 additions and 0 deletions

199
gateware/litex_main.py Executable file
View File

@ -0,0 +1,199 @@
#!/usr/bin/env python3
#
# This file is part of LiteX-Boards.
#
# Copyright (c) 2021 Kazumoto Kojima <kkojima@rr.iij4u.or.jp>
# SPDX-License-Identifier: BSD-2-Clause
from migen import *
from litex.build.io import DDROutput
#from litex_boards.platforms import colorlight_i5
from platforms import sonar as colorlight_i5
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.builder import *
from litex.soc.cores.video import VideoHDMIPHY
from litex.soc.cores.led import LedChaser
from litex.soc.interconnect.csr import *
from litedram.modules import M12L64322A # Compatible with EM638325-6H.
from litedram.phy import GENSDRPHY, HalfRateGENSDRPHY
from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII
from sampler import Sampler
from litex.soc.integration.soc import SoCRegion
# CRG ----------------------------------------------------------------------------------------------
class _CRG(Module):
def __init__(self, platform, sys_clk_freq, use_internal_osc=False, with_usb_pll=False, with_video_pll=False, sdram_rate="1:1"):
self.rst = Signal()
self.clock_domains.cd_sys = ClockDomain()
if sdram_rate == "1:2":
self.clock_domains.cd_sys2x = ClockDomain()
self.clock_domains.cd_sys2x_ps = ClockDomain()
else:
self.clock_domains.cd_sys_ps = ClockDomain()
# # #
# Clk / Rst
if not use_internal_osc:
clk = platform.request("clk25")
clk_freq = 25e6
else:
clk = Signal()
div = 5
self.specials += Instance("OSCG",
p_DIV = div,
o_OSC = clk
)
clk_freq = 310e6/div
#rst_n = platform.request("cpu_reset_n")
# PLL
self.submodules.pll = pll = ECP5PLL()
self.comb += pll.reset.eq(self.rst)
pll.register_clkin(clk, clk_freq)
pll.create_clkout(self.cd_sys, sys_clk_freq)
if sdram_rate == "1:2":
pll.create_clkout(self.cd_sys2x, 2*sys_clk_freq)
pll.create_clkout(self.cd_sys2x_ps, 2*sys_clk_freq, phase=180) # Idealy 90° but needs to be increased.
else:
pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=180) # Idealy 90° but needs to be increased.
# SDRAM clock
sdram_clk = ClockSignal("sys2x_ps" if sdram_rate == "1:2" else "sys_ps")
self.specials += DDROutput(1, 0, platform.request("sdram_clock"), sdram_clk)
# BaseSoC ------------------------------------------------------------------------------------------
# TODO make my own platform for this based on the colorlight one, so I can export I2C and other pins
class BaseSoC(SoCCore):
def __init__(self, sys_clk_freq=60e6, eth_phy=0, with_led_chaser=True, use_internal_osc=False,
sdram_rate="1:1", with_video_terminal=False, with_video_framebuffer=False,
**kwargs):
# TODO change SRAM size
kwargs["integrated_sram_size"] = 64 * 1024
kwargs["integrated_rom_init"] = "../firmware/fw.bin"
platform = colorlight_i5.Platform(board="i9", revision="7.2", toolchain="trellis")
# CRG --------------------------------------------------------------------------------------
with_usb_pll = kwargs.get("uart_name", None) == "usb_acm"
with_video_pll = with_video_terminal or with_video_framebuffer
self.submodules.crg = _CRG(platform, sys_clk_freq,
use_internal_osc = use_internal_osc,
with_usb_pll = with_usb_pll,
with_video_pll = with_video_pll,
sdram_rate = sdram_rate
)
# SoCCore ----------------------------------------------------------------------------------
SoCCore.__init__(self, platform, int(sys_clk_freq), ident = "LiteX SoC on Sonar FPGA", **kwargs)
# Leds -------------------------------------------------------------------------------------
if with_led_chaser:
ledn = platform.request_all("user_led_n")
self.submodules.leds = LedChaser(pads=ledn, sys_clk_freq=sys_clk_freq)
# SPI Flash --------------------------------------------------------------------------------
from litespi.modules import W25Q64 as SpiFlashModule
from litespi.opcodes import SpiNorFlashOpCodes as Codes
self.add_spi_flash(mode="1x", module=SpiFlashModule(Codes.READ_1_1_1))
# SDR SDRAM --------------------------------------------------------------------------------
#if not self.integrated_main_ram_size:
# sdrphy_cls = HalfRateGENSDRPHY if sdram_rate == "1:2" else GENSDRPHY
# self.submodules.sdrphy = sdrphy_cls(platform.request("sdram"))
# self.add_sdram("sdram",
# phy = self.sdrphy,
# module = M12L64322A(sys_clk_freq, sdram_rate),
# l2_cache_size = kwargs.get("l2_size", 8192)
# )
# Ethernet / Etherbone ---------------------------------------------------------------------
self.submodules.ethphy = LiteEthPHYRGMII(
clock_pads = self.platform.request("eth_clocks", eth_phy),
pads = self.platform.request("eth", eth_phy),
tx_delay = 0)
self.add_ethernet(phy=self.ethphy)
# Video ------------------------------------------------------------------------------------
if with_video_terminal or with_video_framebuffer:
self.submodules.videophy = VideoHDMIPHY(platform.request("gpdi"), clock_domain="hdmi")
if with_video_terminal:
self.add_video_terminal(phy=self.videophy, timings="800x600@60Hz", clock_domain="hdmi")
if with_video_framebuffer:
self.add_video_framebuffer(phy=self.videophy, timings="800x600@60Hz", clock_domain="hdmi")
self.submodules.sampler = Sampler(platform.request("adc"))
sampler_region = SoCRegion(origin=None, size=0x1000, cached=False)
#self.add_wb_slave(0x9000_0000, self.sampler.bus, 0x1000)
# TODO better way to do this?
self.bus.add_slave(name="sampler", slave=self.sampler.bus, region=sampler_region)
# Build --------------------------------------------------------------------------------------------
def main():
from litex.soc.integration.soc import LiteXSoCArgumentParser
parser = LiteXSoCArgumentParser(description="LiteX SoC on Colorlight I5")
target_group = parser.add_argument_group(title="Target options")
target_group.add_argument("--build", action="store_true", help="Build design.")
target_group.add_argument("--load", action="store_true", help="Load bitstream.")
target_group.add_argument("--sys-clk-freq", default=60e6, help="System clock frequency.")
sdopts = target_group.add_mutually_exclusive_group()
sdopts.add_argument("--with-spi-sdcard", action="store_true", help="Enable SPI-mode SDCard support.")
sdopts.add_argument("--with-sdcard", action="store_true", help="Enable SDCard support.")
target_group.add_argument("--eth-phy", default=0, type=int, help="Ethernet PHY (0 or 1).")
target_group.add_argument("--use-internal-osc", action="store_true", help="Use internal oscillator.")
target_group.add_argument("--sdram-rate", default="1:1", help="SDRAM Rate (1:1 Full Rate or 1:2 Half Rate).")
viopts = target_group.add_mutually_exclusive_group()
viopts.add_argument("--with-video-terminal", action="store_true", help="Enable Video Terminal (HDMI).")
viopts.add_argument("--with-video-framebuffer", action="store_true", help="Enable Video Framebuffer (HDMI).")
builder_args(parser)
soc_core_args(parser)
trellis_args(parser)
args = parser.parse_args()
# Build firmware
import subprocess as sp
sp.run(["./build_and_strip.sh"], cwd="../firmware").check_returncode()
soc = BaseSoC(
sys_clk_freq = int(float(args.sys_clk_freq)),
eth_phy = args.eth_phy,
use_internal_osc = args.use_internal_osc,
sdram_rate = args.sdram_rate,
with_video_terminal = args.with_video_terminal,
with_video_framebuffer = args.with_video_framebuffer,
**soc_core_argdict(args)
)
soc.platform.add_extension(colorlight_i5._sdcard_pmod_io)
if args.with_spi_sdcard:
soc.add_spi_sdcard()
if args.with_sdcard:
soc.add_sdcard()
builder = Builder(soc, **builder_argdict(args))
builder_kargs = trellis_argdict(args)
if args.build:
builder.build(**builder_kargs)
if args.load:
prog = soc.platform.create_programmer()
prog.load_bitstream(builder.get_bitstream_filename(mode="sram"))
if __name__ == "__main__":
main()

233
gateware/platforms/sonar.py Normal file
View File

@ -0,0 +1,233 @@
"""
LiteX Platform for sonar board using Colorlight i9 module
"""
# This file used to belong to LiteX Boards
#
# Copyright (c) 2021 Kazumoto Kojima <kkojima@rr.iij4u.or.jp>
# Copyright (c) 2023 David Lenfesty <lenfesty@ualberta.ca>
# SPDX-License-Identifier: BSD-2-Clause
# The Colorlight i5 PCB and IOs have been documented by @wuxx
# https://github.com/wuxx/Colorlight-FPGA-Projects
import copy
from litex.build.generic_platform import *
from litex.build.lattice import LatticePlatform
from litex.build.lattice.programmer import EcpDapProgrammer
# IOs ----------------------------------------------------------------------------------------------
_io_v7_0 = [ # Documented by @smunaut
# Clk
("clk25", 0, Pins("P3"), IOStandard("LVCMOS33")),
# Led
("user_led_n", 0, Pins("U16"), IOStandard("LVCMOS33")),
# Serial
("serial", 0,
Subsignal("tx", Pins("P16")),
Subsignal("rx", Pins("L5")),
IOStandard("LVCMOS33")
),
# TODO the other serial ports
# TODO I2C
# SPIFlash (GD25Q16CSIG)
("spiflash", 0,
Subsignal("cs_n", Pins("R2")),
# https://github.com/m-labs/nmigen-boards/pull/38
#Subsignal("clk", Pins("")), driven through USRMCLK
Subsignal("mosi", Pins("W2")),
Subsignal("miso", Pins("V2")),
IOStandard("LVCMOS33"),
),
# SDRAM SDRAM (EM638325-6H)
("sdram_clock", 0, Pins("B9"), IOStandard("LVCMOS33")),
("sdram", 0,
Subsignal("a", Pins(
"B13 C14 A16 A17 B16 B15 A14 A13",
"A12 A11 B12")),
Subsignal("dq", Pins(
"D15 E14 E13 D12 E12 D11 C10 B17",
"B8 A8 C7 A7 A6 B6 A5 B5",
"D5 C5 D6 C6 E7 D7 E8 D8",
"E9 D9 E11 C11 C12 D13 D14 C15")),
Subsignal("we_n", Pins("A10")),
Subsignal("ras_n", Pins("B10")),
Subsignal("cas_n", Pins("A9")),
#Subsignal("cs_n", Pins("")), # gnd
#Subsignal("cke", Pins("")), # 3v3
Subsignal("ba", Pins("B11 C8")), # sdram pin BA0 and BA1
#Subsignal("dm", Pins("")), # gnd
IOStandard("LVCMOS33"),
Misc("SLEWRATE=FAST")
),
# RGMII Ethernet (B50612D)
# The order of the two PHYs is swapped with the naming of the connectors
# on the board so to match with the configuration of their PHYA[0] pins.
("eth_clocks", 0,
Subsignal("tx", Pins("G1")),
Subsignal("rx", Pins("H2")),
IOStandard("LVCMOS33")
),
("eth", 0,
Subsignal("rst_n", Pins("P4")),
Subsignal("mdio", Pins("P5")),
Subsignal("mdc", Pins("N5")),
Subsignal("rx_ctl", Pins("P2")),
Subsignal("rx_data", Pins("K2 L1 N1 P1")),
Subsignal("tx_ctl", Pins("K1")),
Subsignal("tx_data", Pins("G2 H1 J1 J3")),
IOStandard("LVCMOS33")
),
("eth_clocks", 1,
Subsignal("tx", Pins("U19")),
Subsignal("rx", Pins("L19")),
IOStandard("LVCMOS33")
),
("eth", 1,
Subsignal("rst_n", Pins("P4")),
Subsignal("mdio", Pins("P5")),
Subsignal("mdc", Pins("N5")),
Subsignal("rx_ctl", Pins("M20")),
Subsignal("rx_data", Pins("P20 N19 N20 M19")),
Subsignal("tx_ctl", Pins("P19")),
Subsignal("tx_data", Pins("U20 T19 T20 R20")),
IOStandard("LVCMOS33")
),
# GPDI
("gpdi", 0,
Subsignal("clk_p", Pins("J19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
#Subsignal("clk_n", Pins("K19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
Subsignal("data0_p", Pins("G19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
#Subsignal("data0_n", Pins("H20"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
Subsignal("data1_p", Pins("E20"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
#Subsignal("data1_n", Pins("F19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
Subsignal("data2_p", Pins("C20"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
#Subsignal("data2_n", Pins("D19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
),
# High speed parallel ADCs
("adc", 0,
Subsignal("data", Pins("M18 N18 N17 P18 U17 U18 T17 M17 P17 R17")),
# TODO ???? what other pins are changed in 7.2
Subsignal("refclk", Pins("L2")),
Subsignal("oen_b", Pins("K18")),
Subsignal("standby", Pins("C18")),
Subsignal("dfs", Pins("T18")),
Subsignal("otr", Pins("R18")),
IOStandard("LVCMOS33")
),
]
# From https://github.com/wuxx/Colorlight-FPGA-Projects/blob/master/schematic/i5_v6.0-extboard.pdf and
# https://github.com/wuxx/Colorlight-FPGA-Projects/blob/master/doc/i5_extboard_v1.2_pinout.png
_connectors_v7_0 = [
("pmode", "C17 B18 B20 F20 A18 A19 B19 D20"),
("pmodf", "D1 C1 C2 E3 E2 D2 B1 A3"),
]
# ColorLight i9 V 7.2 hardware
# See https://github.com/wuxx/Colorlight-FPGA-Projects/blob/master/colorlight_i9_v7.2.md
# SPIFlash (W25Q64JVSIQ)
_io_v7_2 = copy.deepcopy(_io_v7_0)
# Change the LED pin to "L2"
for i, x in enumerate(_io_v7_2):
if x[:2] == ("user_led_n", 0):
# TODO fix in HW
#_io_v7_2[i] = ("user_led_n", 0, Pins("L2"), IOStandard("LVCMOS33"))
_io_v7_2[i] = ("user_led_n", 0, Pins("J19"), IOStandard("LVCMOS33"))
break
# optional, alternative uart location
# requires "--uart-name serialx"
_io_v7_2 += [
("serialx", 0, Subsignal("tx", Pins("E5")), Subsignal("rx", Pins("F4")), IOStandard("LVCMOS33"))
]
_connectors_v7_2 = copy.deepcopy(_connectors_v7_0)
# Append the rest of the pmod interfaces
_connectors_v7_2 += [
# P2
("pmodc", "P17 R18 C18 L2 M17 R17 T18 K18"),
("pmodd", "J20 L18 M18 N17 G20 K20 L20 N18"),
# P4
("pmodg", "H4 G3 F1 F2 H3 F3 E4 E1"),
("pmodh", "- E19 B3 K5 - B2 K4 A2"),
# P5
("pmodi", "D18 G5 F5 E5 D17 D16 E6 F4"),
("pmodj", "J17 H17 H16 G16 H18 G18 F18 E18"),
# P6
("pmodk", "R3 M4 L5 J16 N4 L4 P16 J18"),
("pmodl", "R1 U1 W1 M1 T1 Y2 V1 N2"),
]
# PMODS --------------------------------------------------------------------------------------------
def sdcard_pmod_io(pmod):
return [
# SDCard PMOD:
# - https://store.digilentinc.com/pmod-microsd-microsd-card-slot/
("spisdcard", 0,
Subsignal("clk", Pins(f"{pmod}:3")),
Subsignal("mosi", Pins(f"{pmod}:1"), Misc("PULLMODE=UP")),
Subsignal("cs_n", Pins(f"{pmod}:0"), Misc("PULLMODE=UP")),
Subsignal("miso", Pins(f"{pmod}:2"), Misc("PULLMODE=UP")),
Misc("SLEWRATE=FAST"),
IOStandard("LVCMOS33"),
),
("sdcard", 0,
Subsignal("data", Pins(f"{pmod}:2 {pmod}:4 {pmod}:5 {pmod}:0"), Misc("PULLMODE=UP")),
Subsignal("cmd", Pins(f"{pmod}:1"), Misc("PULLMODE=UP")),
Subsignal("clk", Pins(f"{pmod}:3")),
Subsignal("cd", Pins(f"{pmod}:6")),
#Misc("SLEWRATE=FAST"),
IOStandard("LVCMOS33"),
),
]
_sdcard_pmod_io = sdcard_pmod_io("pmode") # SDCARD PMOD on P3.
# Platform -----------------------------------------------------------------------------------------
class Platform(LatticePlatform):
default_clk_name = "clk25"
default_clk_period = 1e9/25e6
def __init__(self, board="i5", revision="7.0", toolchain="trellis"):
if board == "i5":
assert revision in ["7.0"]
self.revision = revision
device = {"7.0": "LFE5U-25F-6BG381C"}[revision]
io = {"7.0": _io_v7_0}[revision]
connectors = {"7.0": _connectors_v7_0}[revision]
if board == "i9":
assert revision in ["7.2"]
self.revision = revision
device = {"7.2": "LFE5U-45F-6BG381C"}[revision]
io = {"7.2": _io_v7_2}[revision]
connectors = {"7.2": _connectors_v7_2}[revision]
LatticePlatform.__init__(self, device, io, connectors=connectors, toolchain=toolchain)
def create_programmer(self):
return EcpDapProgrammer()
def do_finalize(self, fragment):
LatticePlatform.do_finalize(self, fragment)
self.add_period_constraint(self.lookup_request("clk25", loose=True), 1e9/25e6)
self.add_period_constraint(self.lookup_request("eth_clocks:rx", 0, loose=True), 1e9/125e6)
self.add_period_constraint(self.lookup_request("eth_clocks:rx", 1, loose=True), 1e9/125e6)

28
gateware/sampler.py Normal file
View File

@ -0,0 +1,28 @@
from migen import *
from litex.soc.interconnect.wishbone import *
from litex.soc.integration.soc import SoCRegion
class Sampler(Module):
def __init__(self, adc_pins):
# TODO correct addr width
self.bus = Interface(data_width=32, adr_width=11)
# self.clock_domains.foo = ClockDomain() is how to add a new clock domain, accessible at self.foo
# Provide a slow clock to the ADC, 60MHz / 600 = 100kHz
self._counter = Signal(32)
self.sync += self._counter.eq(self._counter + 1)
self.sync += If(self._counter >= 600, self._counter.eq(0), adc_pins.refclk.eq(~adc_pins.refclk))
# Set config pins to constant values
self.comb += adc_pins.oen_b.eq(0) # Data pins enable
self.comb += adc_pins.standby.eq(0) # Sampling standby
self.comb += adc_pins.dfs.eq(0) # DFS (raw or two's complement)
# The only remaining pin, OTR, is an out of range status indicator
# Read directly from the data pins into the wishbone bus for now, just for bringup
self.comb += self.bus.dat_r.eq(adc_pins.data)
self.sync += self.bus.ack.eq(0)
self.sync += If(self.bus.cyc & self.bus.stb, self.bus.ack.eq(1))