From 0ce7f8354c5f80f770d8148cf955e067ca52b3b2 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 3 May 2022 19:03:55 +0200 Subject: [PATCH] Add initial LimeSDR Mini V2 support (With SoC + USB3 (FT245PHYSynchronous)). python3 -m litex_boards.targets.limesdr_mini_v2 --csr-csv=csr.csv --build --load litex_server --jtag --jtag-config=openocd_limesdr_mini_v2.cfg litex_term crossover __ _ __ _ __ / / (_) /____ | |/_/ / /__/ / __/ -_)> < /____/_/\__/\__/_/|_| Build your hardware, easily! (c) Copyright 2012-2022 Enjoy-Digital (c) Copyright 2007-2015 M-Labs BIOS built on May 3 2022 18:59:46 BIOS CRC passed (5f29afcc) LiteX git sha1: a4cc859d --=============== SoC ==================-- CPU: VexRiscv @ 80MHz BUS: WISHBONE 32-bit @ 4GiB CSR: 32-bit data ROM: 128KiB SRAM: 8KiB --============== Boot ==================-- Booting from serial... Press Q or ESC to abort boot completely. sL5DdSMmkekro Timeout No boot medium found --============= Console ================-- litex> ident Ident: LiteX SoC on LimeSDR-Mini-V2 2022-05-03 18:59:29 --- README.md | 1 + litex_boards/platforms/limesdr_mini_v2.py | 124 +++++++++++++ litex_boards/prog/openocd_limesdr_mini_v2.cfg | 10 ++ litex_boards/targets/limesdr_mini_v2.py | 165 ++++++++++++++++++ 4 files changed, 300 insertions(+) create mode 100644 litex_boards/platforms/limesdr_mini_v2.py create mode 100644 litex_boards/prog/openocd_limesdr_mini_v2.cfg create mode 100755 litex_boards/targets/limesdr_mini_v2.py diff --git a/README.md b/README.md index 018e5c8..ef12459 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ Some of the suported boards, see yours? Give LiteX-Boards a try! ├── lattice_machxo3 ├── lattice_versa_ecp5 ├── linsn_rv901t + ├── limesdr_mini_v2 ├── litex_acorn_baseboard ├── logicbone ├── marblemini diff --git a/litex_boards/platforms/limesdr_mini_v2.py b/litex_boards/platforms/limesdr_mini_v2.py new file mode 100644 index 0000000..a1e9d2e --- /dev/null +++ b/litex_boards/platforms/limesdr_mini_v2.py @@ -0,0 +1,124 @@ +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2022 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from litex.build.generic_platform import * +from litex.build.lattice import LatticePlatform +from litex.build.lattice.programmer import OpenOCDJTAGProgrammer + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + # Clk. + ("clk40", 0, Pins("A9"), IOStandard("LVCMOS33")), + + # Leds. + ("led_g_n", 0, Pins("R16"), IOStandard("LVCMOS33"), Misc("OPENDRAIN=ON")), + ("led_g_n", 1, Pins("M18"), IOStandard("LVCMOS33"), Misc("OPENDRAIN=ON")), # Shared with FPGA_GPIO4. + ("led_g_n", 2, Pins("T17"), IOStandard("LVCMOS33"), Misc("OPENDRAIN=ON")), # Shared with FPGA_GPIO6. + ("led_r_n", 0, Pins("V17"), IOStandard("LVCMOS33"), Misc("OPENDRAIN=ON")), + ("led_r_n", 1, Pins("R18"), IOStandard("LVCMOS33"), Misc("OPENDRAIN=ON")), # Shared with FPGA_GPIO5. + ("led_r_n", 2, Pins("R17"), IOStandard("LVCMOS33"), Misc("OPENDRAIN=ON")), # Shared with FPGA_GPIO7. + + # Revision. + ("revision", 0, + Subsignal("hardware", Pins("D4 M2 N4 J3")), + Subsignal("bom", Pins("N1 M1 N2")), + IOStandard("LVCMOS25") + ), + + # GPIO. + ("gpio", 0, Pins("N15 N18 N16 N17 M18 R18 T17 R17"), IOStandard("LVCMOS33")), + ("egpio", 0, Pins("A10 A8"), IOStandard("LVCMOS33")), + + # SPIFlash + ("spiflash", 0, + Subsignal("cs_n", Pins("U17")), + Subsignal("clk", Pins("U16")), + Subsignal("miso", Pins("U18")), + Subsignal("mosi", Pins("T18")), + IOStandard("LVCMOS33"), + ), + + # I2C. + ("i2c", 0, + Subsignal("scl", Pins("C10"), Misc("OPENDRAIN=ON")), + Subsignal("sda", Pins("B9"), Misc("OPENDRAIN=ON")), + IOStandard("LVCMOS33"), + ), + + # SPI. + ("spi", 0, + # SPI. + Subsignal("clk", Pins("M3")), + Subsignal("lms_cs_n", Pins("N3")), + Subsignal("dac_cs_n", Pins("L4")), + Subsignal("mosi", Pins("L3")), + Subsignal("miso", Pins("K3")), + IOStandard("LVCMOS25"), + ), + + # Temperature Sensor. + ("lms75_os", 0, Pins("K2"), IOStandard("LVCMOS25")), + + # USB-FIFO. + ("usb_fifo_clk", 0, Pins("D17"), IOStandard("LVCMOS33")), + ("usb_fifo", 0, + Subsignal("rst_n", Pins("M17")), + Subsignal("data", Pins( + "A13 B12 B15 C12 A16 A12 D18 B17", + "F15 D16 D15 C13 H18 B13 J18 A15", + "B18 C18 A17 K18 C15 L18 F18 C16", + "G16 D13 G18 F16 C17 F17 K15 K17")), + Subsignal("be", Pins("L15 J17 K16 H17")), + Subsignal("rxf_n", Pins("H16")), + Subsignal("txe_n", Pins("M16")), + Subsignal("rd_n", Pins("H15")), + Subsignal("wr_n", Pins("J16")), + Subsignal("oe_n", Pins("L16")), + IOStandard("LVCMOS33"), + ), + + # RF-IC / LMS7002M. + ("lms7002m", 0, + # Control. + Subsignal("pwrdwn_n", Pins("C8")), + Subsignal("rxen", Pins("D6")), + Subsignal("txen", Pins("B7")), + + # RX-Interface (LMS -> FPGA). + Subsignal("diq1", Pins("J2 L1 K1 K4 G3 F4 J1 H1 G4 F2 G1 H2")), + Subsignal("txnrx1", Pins("F1")), + Subsignal("iqsel1", Pins("F3")), + Subsignal("mclk1", Pins("H4")), + Subsignal("fclk1", Pins("H3")), + + # RX-Interface (FPGA -> LMS). + Subsignal("diq2", Pins("A3 C2 A2 B4 C3 B2 D3 B1 A4 C1 C7 A6")), + Subsignal("txnrx2", Pins("B6")), + Subsignal("iqsel2", Pins("C4")), + Subsignal("mclk2", Pins("D2")), + Subsignal("fclk2", Pins("D1")), + + # IOStandard/Slew Rate. + IOStandard("LVCMOS25"), + ), +] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(LatticePlatform): + default_clk_name = "clk40" + default_clk_period = 1e9/40e6 + + def __init__(self, device="LFE5U", toolchain="trellis", **kwargs): + assert device in ["LFE5U"] + LatticePlatform.__init__(self, device + "-45F-8MG285C", _io, toolchain=toolchain, **kwargs) + + def create_programmer(self): + return OpenOCDJTAGProgrammer("openocd_limesdr_mini_v2.cfg") + + def do_finalize(self, fragment): + self.add_period_constraint(self.lookup_request("clk40", loose=True), 1e9/40e6) diff --git a/litex_boards/prog/openocd_limesdr_mini_v2.cfg b/litex_boards/prog/openocd_limesdr_mini_v2.cfg new file mode 100644 index 0000000..b3e348d --- /dev/null +++ b/litex_boards/prog/openocd_limesdr_mini_v2.cfg @@ -0,0 +1,10 @@ +interface ftdi +ftdi_vid_pid 0x0403 0x6010 +ftdi_channel 0 +ftdi_layout_init 0xfff8 0xfffb +reset_config none + +adapter_khz 25000 + +set _CHIPNAME ecp5 +jtag newtap ecp5 tap -irlen 8 -expected-id 0x41112043 diff --git a/litex_boards/targets/limesdr_mini_v2.py b/litex_boards/targets/limesdr_mini_v2.py new file mode 100755 index 0000000..44418bf --- /dev/null +++ b/litex_boards/targets/limesdr_mini_v2.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 + +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2022 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +# Build/Use: +# ./limesdr_mini_v2.py --csr-csv=csr.csv --build --load +# litex_server --jtag --jtag-config=openocd_limesdr_mini_v2.cfg +# litex_term crossover + +from migen import * + +from litex_boards.platforms import limesdr_mini_v2 + +from litex.build.lattice.trellis import trellis_args, trellis_argdict + +from litex.soc.cores.clock import * +from litex.soc.interconnect.csr import * +from litex.soc.integration.soc_core import * +from litex.soc.integration.builder import * +from litex.soc.interconnect import stream + +from litex.soc.cores.led import LedChaser +from litex.soc.cores.bitbang import I2CMaster +from litex.soc.cores.usb_fifo import FT245PHYSynchronous + +from litescope import LiteScopeAnalyzer + +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG(Module): + def __init__(self, platform, sys_clk_freq): + self.rst = Signal() + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_usb = ClockDomain() + + # # # + + # Clk. + clk40 = platform.request("clk40") + + # PLL. + self.submodules.pll = pll = ECP5PLL() + self.comb += pll.reset.eq(self.rst) + pll.register_clkin(clk40, 40e6) + pll.create_clkout(self.cd_sys, sys_clk_freq) + + # USB. + self.comb += self.cd_usb.clk.eq(platform.request("usb_fifo_clk")) + +# BoardInfo ---------------------------------------------------------------------------------------- + +class BoardInfo(Module, AutoCSR): + def __init__(self, revision_pads): + self.revision = CSRStorage(fields=[ + CSRField("hardware", size=4, description="Hardware Revision."), + CSRField("bom", size=4, description="Bill of Material Revision."), + ]) + + # # # + + self.comb += self.revision.fields.hardware.eq(revision_pads.hardware) + self.comb += self.revision.fields.bom .eq(revision_pads.bom) + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + def __init__(self, sys_clk_freq=int(80e6), toolchain="trellis", + with_usb_fifo = True, with_usb_fifo_loopback=False, + with_led_chaser = True, + **kwargs): + platform = limesdr_mini_v2.Platform(toolchain=toolchain) + + # SoCCore ---------------------------------------------------------------------------------- + kwargs["uart_name"] = "crossover" + SoCCore.__init__(self, platform, sys_clk_freq, ident="LiteX SoC on LimeSDR-Mini-V2", **kwargs) + + # JTAGBone --------------------------------------------------------------------------------- + self.add_jtagbone() + + # CRG -------------------------------------------------------------------------------------- + self.submodules.crg = _CRG(platform, sys_clk_freq) + + # Info ------------------------------------------------------------------------------------- + self.submodules.info = BoardInfo(platform.request("revision")) + + # I2C Bus ---------------------------------------------------------------------------------- + # - Temperature Sensor (LM72 @ 0x48). + # - Eeprom (M24128 @ 0x50) / Not populated. + self.submodules.i2c = I2CMaster(platform.request("i2c")) + + # USB-FIFO --------------------------------------------------------------------------------- + if with_usb_fifo: + usb_pads = platform.request("usb_fifo") + self.submodules.usb_phy = usb_phy = FT245PHYSynchronous( + pads = usb_pads, + clk_freq = sys_clk_freq, + fifo_depth = 8, + read_time = 128, + write_time = 128, + ) + if with_usb_fifo_loopback: + usb_loopback = stream.SyncFIFO([("data", 32)], 2048, buffered=True) + self.submodules += usb_loopback + self.comb += [ + usb_phy.source.connect(usb_loopback.sink), + usb_loopback.source.connect(usb_phy.sink), + ] + else: + self.comb += usb_phy.source.ready.eq(1) # Accept incoming stream to validate Host -> FPGA. + + analyzer_probes = usb_phy.get_litescope_probes() + self.submodules.analyzer = LiteScopeAnalyzer(analyzer_probes, + depth = 512, + clock_domain = "usb", + samplerate = sys_clk_freq, + csr_csv = "analyzer.csv" + ) + + # Debug ------------------------------------------------------------------------------- + egpio_pads = platform.request("egpio") + self.comb += egpio_pads[0].eq(ClockSignal("sys")) + self.comb += egpio_pads[1].eq(ClockSignal("usb")) + + # Leds ------------------------------------------------------------------------------------- + if with_led_chaser: + leds_g = Signal(4) + leds_r = Signal(4) + self.comb += platform.request_all("led_g_n").eq(~leds_g) + self.comb += platform.request_all("led_r_n").eq(~leds_r) + self.submodules.leds = LedChaser(Cat(leds_g, leds_r), sys_clk_freq) + +# Build -------------------------------------------------------------------------------------------- + +def main(): + from litex.soc.integration.soc import LiteXSoCArgumentParser + parser = LiteXSoCArgumentParser(description="LiteX SoC on LimeSDR-Mini-V2") + target_group = parser.add_argument_group(title="Target options") + target_group.add_argument("--build", action="store_true", help="Build bitstream.") + target_group.add_argument("--load", action="store_true", help="Load bitstream.") + target_group.add_argument("--toolchain", default="trellis", help="FPGA toolchain (trellis or diamond).") + target_group.add_argument("--sys-clk-freq", default=80e6, help="System clock frequency.") + builder_args(parser) + soc_core_args(parser) + trellis_args(parser) + args = parser.parse_args() + + soc = BaseSoC( + sys_clk_freq = int(float(args.sys_clk_freq)), + toolchain = args.toolchain, + **soc_core_argdict(args) + ) + 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(builder.get_bitstream_filename(mode="sram", ext=".svf")) # FIXME + +if __name__ == "__main__": + main()