A Neopixel solution for Colorlight boards ...
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Neopixelar/neopixelar.py

420 lines
20 KiB

#!/usr/bin/env python3
#
# neopixelar.py
#
# This file has been derived from LiteX-Boards/colorlight_5b_75x.py
# Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
#
# History:
# --------
# 14.10.20/KQ Initial version, some output ports activated (user_led, J4) for testing
# 15.10.20/KQ Own logic exported to external module (neopixelprotocol.py - now neopixelengine.py)
# 19.10.20/KQ Project renamed 'NeoPixelar' (swedish plural for NeoPixel ...)
#
# Build/Use ----------------------------------------------------------------------------------------
# - 'python3 neopixelar.py --build --revision=7.0 --uart-name=crossover --with-etherbone" --csr-csv=build/csr.csv'
# to generate
# - 'python3 neopixelar.py --load' to download to FPGA
# - 'ping 192.168.1.50' to verify ethernet connection - via LEFT(!) RJ45 port
# - 'wishbone-tool --ethernet-host 192.168.1.50 --server terminal --csr-csv build/csr.csv'
# You should see the LiteX BIOS and be able to interact with it
#
# 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...
import os
import argparse
import sys
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.build.io import DDROutput
from litex_boards.platforms import colorlight_5a_75b, colorlight_5a_75e
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 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
# 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, sys_clk_freq=60e6, use_internal_osc=False, sdram_rate="1:1", **kwargs):
board = board.lower()
assert board in ["5a-75b", "5a-75e"]
if board == "5a-75b":
platform = colorlight_5a_75b.Platform(revision=revision)
elif board == "5a-75e":
platform = colorlight_5a_75e.Platform(revision=revision)
if board == "5a-75e" and revision == "6.0" and (with_etherbone or with_ethernet):
assert use_internal_osc, "You cannot use the 25MHz clock as system clock since it is provided by the Ethernet PHY and will stop during PHY reset."
# 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)
# Base counter (used for clocking)
counter = Signal(32) # 32-Bit counter
self.sync += counter.eq(counter + 1)
# USERLED blink (on-board LED)
self.comb += platform.request("user_led_n").eq(counter[23]) # ~2Hz (?)
platform.add_extension(_gpios) # General LED outputs
self.submodules.npe = NeoPixelEngine()
self.add_csr("npe")
# Inputs 1st
self.comb += self.npe.b24GRBArray[0].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[1].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[2].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[3].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[4].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[5].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[6].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[7].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[8].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[9].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[10].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[11].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[12].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[13].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[14].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[15].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[16].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[17].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[18].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[19].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[20].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[21].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[22].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[23].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[24].eq(0x330000) # 1st: G
self.comb += self.npe.b24GRBArray[25].eq(0x004400) # 2nd: R
self.comb += self.npe.b24GRBArray[26].eq(0x000055) # 3rd: B
self.comb += self.npe.b24GRBArray[27].eq(0x330000) # 1st: G
self.comb += self.npe.b8Len.eq(27) # 27 24-bit 'words'
self.comb += self.npe.bStart.eq(True) # Start pulse
# Outputs 2nd
# Do output on J4
for i in range(42,56):
self.comb += platform.request("gpio", i).eq(self.npe.bDataPin) # Output data pin
#self.comb += platform.request("gpio", 42).eq(counter[1]) # For measurement of freq#!
#self.comb += platform.request("gpio", 43).eq(counter[23]) # Test LEDs
#self.comb += platform.request("gpio", 46).eq(counter[22])
# 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("--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")
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,
sys_clk_freq = args.sys_clk_freq,
use_internal_osc = args.use_internal_osc,
sdram_rate = args.sdram_rate,
**soc_core_argdict(args))
builder = Builder(soc, **builder_argdict(args))
builder.build(**trellis_argdict(args), run=args.build)
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"))
if __name__ == "__main__":
main()