#!/usr/bin/env python3 # # neopixelar.py # # This file has been derived from LiteX-Boards/colorlight_5b_75x.py # Copyright (c) 2020 Florent Kermarrec # SPDX-License-Identifier: BSD-2-Clause # # Disclaimer: Still a proof of concept with large timings violations on the IP/UDP and # Etherbone stack that need to be optimized. It was initially just used to validate the reversed # pinout but happens to work on hardware... # # History: # -------- # 14.09.20/KQ Initial version, some output ports activated (user_led, J4) for testing # 15.09.20/KQ Own logic exported to external module (neopixelprotocol.py - now neopixelengine.py) # 19.09.20/KQ Project renamed 'NeoPixelar' (swedish plural for NeoPixel ...) # 16.10.20/KQ UART w/o crossover now, requires serial terminal # # Build/Use ---------------------------------------------------------------------------------------- # - 'python3 neopixelar.py --build --revision=7.0 --uart-name=crossover --with-etherbone --ip-address=192.168.1.20 --csr-csv=build/csr.csv' # to generate # - 'python3 neopixelar.py --load' to download to FPGA # - 'ping 192.168.1.20' to verify ethernet connection - via LEFT(!) RJ45 port # - 'wishbone-tool --ethernet-host 192.168.1.20 --server terminal --csr-csv build/csr.csv' # You should see the LiteX BIOS and be able to interact with it # - To load a file to RAM (@0x40000000 len=0x400000) use: # wishbone-tool --ethernet-host 192.168.1.20 --server load-file --csr-csv build/csr.csv # --load-address 0x40100000 # --load-name build/colorlight_5a_75b/software/ # To disassemble raw file: # ../fpga/litex/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-objdump # -D -b binary ./build/colorlight_5a_75b/software/bios/bios.bin -m riscv # import os import argparse import sys from migen import * from migen.genlib.misc import WaitTimer from migen.genlib.resetsync import AsyncResetSynchronizer from litex.build.io import DDROutput from litex_boards.platforms import colorlight_5a_75b from litex.build.lattice.trellis import trellis_args, trellis_argdict from litex.soc.cores.clock import * from litex.soc.cores.spi_flash import ECP5SPIFlash from litex.soc.cores.gpio import GPIOOut from litex.soc.integration.soc_core import * from litex.soc.integration.builder import * from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField from litedram.modules import M12L16161A, M12L64322A from litedram.phy import GENSDRPHY, HalfRateGENSDRPHY from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII from litex.build.generic_platform import * import litex.soc.doc as lxsocdoc from neopixelengine import NeoPixelEngine from helpers.prepare_firmware import copyjob # Explicit IO naming, taken from board colorlight 5a 75b board defs. (rev. 7.0) # from https://github.com/q3k/chubby75/blob/master/5a-75b/hardware_V7.0.md #_connectors_v7_0 = [ # ("j1", "F3 F1 G3 - G2 H3 H5 F15 L2 K1 J5 K2 B16 J14 F12 -"), # ("j2", "J4 K3 G1 - K4 C2 E3 F15 L2 K1 J5 K2 B16 J14 F12 -"), # ("j3", "H4 K5 P1 - R1 L5 F2 F15 L2 K1 J5 K2 B16 J14 F12 -"), # ("j4", "P4 R2 M8 - M9 T6 R6 F15 L2 K1 J5 K2 B16 J14 F12 -"), # ("j5", "M11 N11 P12 - K15 N12 L16 F15 L2 K1 J5 K2 B16 J14 F12 -"), # ("j6", "K16 J15 J16 - J12 H15 G16 F15 L2 K1 J5 K2 B16 J14 F12 -"), # ("j7", "H13 J13 H12 - G14 H14 G15 F15 L2 K1 J5 K2 B16 J14 F12 -"), # ("j8", "A15 F16 A14 - E13 B14 A13 F15 L2 K1 J5 K2 B16 J14 F12 -"), #] _gpios = [ # Attn. Jx/pin descriptions are 1-based, but zero based defs. used! # J1 ("gpio", 0, Pins("j1:0"), IOStandard("LVCMOS33")), ("gpio", 1, Pins("j1:1"), IOStandard("LVCMOS33")), ("gpio", 2, Pins("j1:2"), IOStandard("LVCMOS33")), # GND ("gpio", 3, Pins("j1:4"), IOStandard("LVCMOS33")), ("gpio", 4, Pins("j1:5"), IOStandard("LVCMOS33")), ("gpio", 5, Pins("j1:6"), IOStandard("LVCMOS33")), ("gpio", 6, Pins("j1:7"), IOStandard("LVCMOS33")), ("gpio", 7, Pins("j1:8"), IOStandard("LVCMOS33")), ("gpio", 8, Pins("j1:9"), IOStandard("LVCMOS33")), ("gpio", 9, Pins("j1:10"), IOStandard("LVCMOS33")), ("gpio", 10, Pins("j1:11"), IOStandard("LVCMOS33")), ("gpio", 11, Pins("j1:12"), IOStandard("LVCMOS33")), ("gpio", 12, Pins("j1:13"), IOStandard("LVCMOS33")), ("gpio", 13, Pins("j1:14"), IOStandard("LVCMOS33")), # GND # J2 ("gpio", 14, Pins("j2:0"), IOStandard("LVCMOS33")), ("gpio", 15, Pins("j2:1"), IOStandard("LVCMOS33")), ("gpio", 16, Pins("j2:2"), IOStandard("LVCMOS33")), # GND ("gpio", 17, Pins("j2:4"), IOStandard("LVCMOS33")), ("gpio", 18, Pins("j2:5"), IOStandard("LVCMOS33")), ("gpio", 19, Pins("j2:6"), IOStandard("LVCMOS33")), ("gpio", 20, Pins("j2:7"), IOStandard("LVCMOS33")), ("gpio", 21, Pins("j2:8"), IOStandard("LVCMOS33")), ("gpio", 22, Pins("j2:9"), IOStandard("LVCMOS33")), ("gpio", 23, Pins("j2:10"), IOStandard("LVCMOS33")), ("gpio", 24, Pins("j2:11"), IOStandard("LVCMOS33")), ("gpio", 25, Pins("j2:12"), IOStandard("LVCMOS33")), ("gpio", 26, Pins("j2:13"), IOStandard("LVCMOS33")), ("gpio", 27, Pins("j2:14"), IOStandard("LVCMOS33")), # GND # J3 ("gpio", 28, Pins("j3:0"), IOStandard("LVCMOS33")), ("gpio", 29, Pins("j3:1"), IOStandard("LVCMOS33")), ("gpio", 30, Pins("j3:2"), IOStandard("LVCMOS33")), # GND ("gpio", 31, Pins("j3:4"), IOStandard("LVCMOS33")), ("gpio", 32, Pins("j3:5"), IOStandard("LVCMOS33")), ("gpio", 33, Pins("j3:6"), IOStandard("LVCMOS33")), ("gpio", 34, Pins("j3:7"), IOStandard("LVCMOS33")), ("gpio", 35, Pins("j3:8"), IOStandard("LVCMOS33")), ("gpio", 36, Pins("j3:9"), IOStandard("LVCMOS33")), ("gpio", 37, Pins("j3:10"), IOStandard("LVCMOS33")), ("gpio", 38, Pins("j3:11"), IOStandard("LVCMOS33")), ("gpio", 39, Pins("j3:12"), IOStandard("LVCMOS33")), ("gpio", 40, Pins("j3:13"), IOStandard("LVCMOS33")), ("gpio", 41, Pins("j3:14"), IOStandard("LVCMOS33")), # GND # J4 ("gpio", 42, Pins("j4:0"), IOStandard("LVCMOS33")), # j4 pin 1 ("gpio", 43, Pins("j4:1"), IOStandard("LVCMOS33")), # j4 pin 2 ("gpio", 44, Pins("j4:2"), IOStandard("LVCMOS33")), # j4 pin 3 # j4 pin 4, GND ("gpio", 45, Pins("j4:4"), IOStandard("LVCMOS33")), # j4 pin 5 ("gpio", 46, Pins("j4:5"), IOStandard("LVCMOS33")), # j4 pin 6 ("gpio", 47, Pins("j4:6"), IOStandard("LVCMOS33")), # j4 pin 7 ("gpio", 48, Pins("j4:7"), IOStandard("LVCMOS33")), # j4 pin 8 ("gpio", 49, Pins("j4:8"), IOStandard("LVCMOS33")), # j4 pin 9 ("gpio", 50, Pins("j4:9"), IOStandard("LVCMOS33")), # j4 pin 10 ("gpio", 51, Pins("j4:10"), IOStandard("LVCMOS33")), # j4 pin 11 ("gpio", 52, Pins("j4:11"), IOStandard("LVCMOS33")), # j4 pin 12 ("gpio", 53, Pins("j4:12"), IOStandard("LVCMOS33")), # j4 pin 13 ("gpio", 54, Pins("j4:13"), IOStandard("LVCMOS33")), # j4 pin 14 ("gpio", 55, Pins("j4:14"), IOStandard("LVCMOS33")), # j4 pin 15 # j4 pin 16, GND # J5 ("gpio", 56, Pins("j5:0"), IOStandard("LVCMOS33")), ("gpio", 57, Pins("j5:1"), IOStandard("LVCMOS33")), ("gpio", 58, Pins("j5:2"), IOStandard("LVCMOS33")), # GND ("gpio", 59, Pins("j5:4"), IOStandard("LVCMOS33")), ("gpio", 60, Pins("j5:5"), IOStandard("LVCMOS33")), ("gpio", 61, Pins("j5:6"), IOStandard("LVCMOS33")), ("gpio", 62, Pins("j5:7"), IOStandard("LVCMOS33")), ("gpio", 63, Pins("j5:8"), IOStandard("LVCMOS33")), ("gpio", 64, Pins("j5:9"), IOStandard("LVCMOS33")), ("gpio", 65, Pins("j5:10"), IOStandard("LVCMOS33")), ("gpio", 66, Pins("j5:11"), IOStandard("LVCMOS33")), ("gpio", 67, Pins("j5:12"), IOStandard("LVCMOS33")), ("gpio", 68, Pins("j5:13"), IOStandard("LVCMOS33")), ("gpio", 69, Pins("j5:14"), IOStandard("LVCMOS33")), # GND # J6 ("gpio", 70, Pins("j6:0"), IOStandard("LVCMOS33")), ("gpio", 71, Pins("j6:1"), IOStandard("LVCMOS33")), ("gpio", 72, Pins("j6:2"), IOStandard("LVCMOS33")), # GND ("gpio", 73, Pins("j6:4"), IOStandard("LVCMOS33")), ("gpio", 74, Pins("j6:5"), IOStandard("LVCMOS33")), ("gpio", 75, Pins("j6:6"), IOStandard("LVCMOS33")), ("gpio", 76, Pins("j6:7"), IOStandard("LVCMOS33")), ("gpio", 77, Pins("j6:8"), IOStandard("LVCMOS33")), ("gpio", 78, Pins("j6:9"), IOStandard("LVCMOS33")), ("gpio", 79, Pins("j6:10"), IOStandard("LVCMOS33")), ("gpio", 80, Pins("j6:11"), IOStandard("LVCMOS33")), ("gpio", 81, Pins("j6:12"), IOStandard("LVCMOS33")), ("gpio", 82, Pins("j6:13"), IOStandard("LVCMOS33")), ("gpio", 83, Pins("j6:14"), IOStandard("LVCMOS33")), # GND # J7 ("gpio", 84, Pins("j7:0"), IOStandard("LVCMOS33")), ("gpio", 85, Pins("j7:1"), IOStandard("LVCMOS33")), ("gpio", 86, Pins("j7:2"), IOStandard("LVCMOS33")), # GND ("gpio", 87, Pins("j7:4"), IOStandard("LVCMOS33")), ("gpio", 88, Pins("j7:5"), IOStandard("LVCMOS33")), ("gpio", 89, Pins("j7:6"), IOStandard("LVCMOS33")), ("gpio", 90, Pins("j7:7"), IOStandard("LVCMOS33")), ("gpio", 91, Pins("j7:8"), IOStandard("LVCMOS33")), ("gpio", 92, Pins("j7:9"), IOStandard("LVCMOS33")), ("gpio", 93, Pins("j7:10"), IOStandard("LVCMOS33")), ("gpio", 94, Pins("j7:11"), IOStandard("LVCMOS33")), ("gpio", 95, Pins("j7:12"), IOStandard("LVCMOS33")), ("gpio", 96, Pins("j7:13"), IOStandard("LVCMOS33")), ("gpio", 97, Pins("j7:14"), IOStandard("LVCMOS33")), # GND # J8 ("gpio", 98, Pins("j8:0"), IOStandard("LVCMOS33")), ("gpio", 99, Pins("j8:1"), IOStandard("LVCMOS33")), ("gpio", 100, Pins("j8:2"), IOStandard("LVCMOS33")), # GND ("gpio", 101, Pins("j8:4"), IOStandard("LVCMOS33")), ("gpio", 102, Pins("j8:5"), IOStandard("LVCMOS33")), ("gpio", 103, Pins("j8:6"), IOStandard("LVCMOS33")), ("gpio", 104, Pins("j8:7"), IOStandard("LVCMOS33")), ("gpio", 105, Pins("j8:8"), IOStandard("LVCMOS33")), ("gpio", 106, Pins("j8:9"), IOStandard("LVCMOS33")), ("gpio", 107, Pins("j8:10"), IOStandard("LVCMOS33")), ("gpio", 108, Pins("j8:11"), IOStandard("LVCMOS33")), ("gpio", 109, Pins("j8:12"), IOStandard("LVCMOS33")), ("gpio", 110, Pins("j8:13"), IOStandard("LVCMOS33")), ("gpio", 111, Pins("j8:14"), IOStandard("LVCMOS33")), # GND ] # CRG ---------------------------------------------------------------------------------------------- class _CRG(Module): def __init__(self, platform, sys_clk_freq, use_internal_osc=False, with_usb_pll=False, with_rst=True, sdram_rate="1:1"): self.clock_domains.cd_sys = ClockDomain() if sdram_rate == "1:2": self.clock_domains.cd_sys2x = ClockDomain() self.clock_domains.cd_sys2x_ps = ClockDomain(reset_less=True) else: self.clock_domains.cd_sys_ps = ClockDomain(reset_less=True) # # # # 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 = 1 if not with_rst else platform.request("user_btn_n", 0) # PLL self.submodules.pll = pll = ECP5PLL() self.comb += pll.reset.eq(~rst_n) 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. # USB PLL if with_usb_pll: self.submodules.usb_pll = usb_pll = ECP5PLL() self.comb += usb_pll.reset.eq(~rst_n) usb_pll.register_clkin(clk, clk_freq) self.clock_domains.cd_usb_12 = ClockDomain() self.clock_domains.cd_usb_48 = ClockDomain() usb_pll.create_clkout(self.cd_usb_12, 12e6, margin=0) usb_pll.create_clkout(self.cd_usb_48, 48e6, margin=0) # 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 ------------------------------------------------------------------------------------------ class BaseSoC(SoCCore): def __init__(self, board, revision, with_ethernet=False, with_etherbone=False, eth_phy=0, ip_address=None, mac_address=None, sys_clk_freq=60e6, use_internal_osc=False, sdram_rate="1:1", **kwargs): platform = colorlight_5a_75b.Platform(revision="7.0") # SoCCore ---------------------------------------------------------------------------------- SoCCore.__init__(self, platform, int(sys_clk_freq), ident = "LiteX SoC on Colorlight " + board.upper(), ident_version = True, **kwargs) # CRG -------------------------------------------------------------------------------------- with_rst = kwargs["uart_name"] not in ["serial", "bridge"] # serial_rx shared with user_btn_n. with_usb_pll = kwargs.get("uart_name", None) == "usb_acm" self.submodules.crg = _CRG(platform, sys_clk_freq, use_internal_osc=use_internal_osc, with_usb_pll=with_usb_pll,with_rst=with_rst, sdram_rate=sdram_rate) # 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")) if board == "5a-75e" and revision == "6.0": sdram_cls = M12L64322A sdram_size = 0x80000000 else: sdram_cls = M12L16161A sdram_size = 0x40000000 self.add_sdram("sdram", phy = self.sdrphy, module = sdram_cls(sys_clk_freq, sdram_rate), origin = self.mem_map["main_ram"], size = kwargs.get("max_sdram_size", sdram_size), 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 / Etherbone --------------------------------------------------------------------- if with_ethernet or with_etherbone: self.submodules.ethphy = LiteEthPHYRGMII( clock_pads = self.platform.request("eth_clocks", eth_phy), pads = self.platform.request("eth", eth_phy)) self.add_csr("ethphy") if with_ethernet: self.add_ethernet(phy=self.ethphy) if with_etherbone: self.add_etherbone( phy=self.ethphy, ip_address = ip_address, mac_address = mac_address, ) # Base counter (used for clocking) counter = Signal(32) # 32-Bit counter self.sync += counter.eq(counter + 1) # USERLED blink (on-board LED) # only w/ uart-name=crossover option: if kwargs["uart_name"] not in ["serial", "bridge"]: self.comb += platform.request("user_led_n").eq(counter[23]) # ~2Hz (?) platform.add_extension(_gpios) # General LED outputs # Adjust no. for your actual project ... max_TABLES = 3 # 1..16 max_LEDS_per_chain = 27 # 1..256 self.submodules.npe = NeoPixelEngine(n_TABLES=max_TABLES, n_LEDs=max_LEDS_per_chain) self.add_csr("npe") for i in range(42,56+2): # Example: Do output on J4 (14) & J5 (2) self.comb += platform.request("gpio", i).eq(self.npe.bDataPin[i-42]) # Output data pin # Build -------------------------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser(description="LiteX SoC on Colorlight 5A-75X") builder_args(parser) soc_core_args(parser) trellis_args(parser) parser.add_argument("--build", action="store_true", help="Build bitstream") parser.add_argument("--load", action="store_true", help="Load bitstream") parser.add_argument("--board", default="5a-75b", help="Board type: 5a-75b (default) & don't change!") parser.add_argument("--revision", default="7.0", type=str, help="Board revision 7.0 (default) & don't change!") parser.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support") parser.add_argument("--with-etherbone", action="store_true", help="Enable Etherbone support") parser.add_argument("--eth-phy", default=0, type=int, help="Ethernet PHY 0 or 1 (default=0)") parser.add_argument("--ip-address", default="192.168.1.50", help="Ethernet IP address of the board.") parser.add_argument("--mac-address", default="0x726b895bc2e2", help="Ethernet MAC address of the board.") parser.add_argument("--sys-clk-freq", default=60e6, type=float, help="System clock frequency (default=60MHz)") parser.add_argument("--use-internal-osc", action="store_true", help="Use internal oscillator") parser.add_argument("--sdram-rate", default="1:1", help="SDRAM Rate 1:1 Full Rate (default), 1:2 Half Rate") parser.add_argument("--csr_csv", default="build/csr.csv", help="CSR list location") parser.add_argument("--doc", action="store_true", help="Create doc files for sphinx generator") parser.add_argument("--flash", action="store_true", help="Load bitstream to flash") args = parser.parse_args() #assert not (args.with_ethernet and args.with_etherbone) soc = BaseSoC(board=args.board, revision=args.revision, with_ethernet = args.with_ethernet, with_etherbone = args.with_etherbone, eth_phy = args.eth_phy, ip_address = args.ip_address, mac_address = int(args.mac_address, 0), sys_clk_freq = args.sys_clk_freq, use_internal_osc = args.use_internal_osc, sdram_rate = args.sdram_rate, **soc_core_argdict(args)) # 32MBit SPIFlash (not used currently) --------------------------------------------------------------- # soc.mem_map["spiflash"] = 0xc0000000 # Boot at +1MB # soc.add_constant("FLASH_BOOT_ADDRESS", soc.mem_map["spiflash"] + 1024*1024) # soc.add_spi_flash(name="spiflash", mode="1x", dummy_cycles=8, clk_freq=5e6) builder = Builder(soc, **builder_argdict(args)) builder.build(**trellis_argdict(args), run=args.build) # Written here to (local) build tree if args.doc: print("Generating documentation for sphinx ...") lxsocdoc.generate_docs(soc, "build/documentation/", project_name="neopixelar", author="KQ") print("Generate via: 'sphinx-build -b html build/documentation build/documentation/html'") if args.load: prog = soc.platform.create_programmer() prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".svf")) return if args.flash: # Convert Bit File to Jtag Write Flash command name = os.path.join(builder.gateware_dir, soc.build_name) print(f"Executing ./bit_to_flash.py {name}.bit {name}.svf.flash") from helpers.bit_to_flash import convertBitToFlashFile convertBitToFlashFile(name + ".bit", name + ".svf.flash") from helpers.load_to_flash import load2flash load2flash(name + ".svf.flash") return if __name__ == "__main__": copyjob() # Create backup if nec. & move our firmware to the correct location main() # Maybe revoke backup action here ...