Memory solution max. is 4x 256, but works!

master
kaqu 2 years ago
parent 98e3d968d7
commit 332a805a0c
  1. 20
      firmware/main.c
  2. 32
      helpers/load_to_flash.py
  3. 81
      neopixelar.py
  4. 419
      neopixelar_simple_72PCT.py
  5. 72
      neopixelengine.py
  6. 215
      neopixelengine_simple_72PCT.py
  7. 84059
      npe.vcd

@ -75,15 +75,27 @@ static int32_t arLEDBuffer[MAXLEDS]; // GRB values
void enable_LEDS(int iEnable)
{
npe_b8Len_write(MAXLEDS); // Prepare length
npe_bEnable_write(iEnable?1:0); // Enable/disable
npe1_b8Len_write(MAXLEDS); // Prepare length
npe1_bEnable_write(iEnable?1:0); // Enable/disable
npe2_b8Len_write(MAXLEDS); // Prepare length
npe2_bEnable_write(iEnable?1:0); // Enable/disable
npe3_b8Len_write(MAXLEDS); // Prepare length
npe3_bEnable_write(iEnable?1:0); // Enable/disable
npe4_b8Len_write(MAXLEDS); // Prepare length
npe4_bEnable_write(iEnable?1:0); // Enable/disable
}
void send_LEDs()
{
for(int i=0;i<MAXLEDS;i++) {
npe_b8LoadOffset_write(i); // @Offset
npe_b24Data2Load_write(arLEDBuffer[i]); // store 32(24) bit value
npe1_b8LoadOffset_write(i); // @Offset
npe1_b24Data2Load_write(arLEDBuffer[i]); // store 32(24) bit value
npe2_b8LoadOffset_write(i); // @Offset
npe2_b24Data2Load_write(arLEDBuffer[i]); // store 32(24) bit value
npe3_b8LoadOffset_write(i); // @Offset
npe3_b24Data2Load_write(arLEDBuffer[i]); // store 32(24) bit value
npe4_b8LoadOffset_write(i); // @Offset
npe4_b24Data2Load_write(arLEDBuffer[i]); // store 32(24) bit value
}
busy_wait(25); // Minimum that meets the eye ;)
}

@ -0,0 +1,32 @@
#!/usr/bin/env python3
#
# load_to_flash.py
# Load/flash helper
#
import os
def load2flash(file):
print("--------")
f = open("prog/openocd.cfg", "w")
f.write(
"""
interface vsllink
adapter_khz 100
reset_config none
jtag newtap ecp5 tap -irlen 8 -expected-id 0x41111043
""")
f.close()
os.system(f"openocd -f prog/openocd.cfg -c \"transport select jtag; init; svf {file}; exit\"")
if __name__ == "__main__":
_, tail = os.path.split(os.getcwd())
if tail == "helpers": # Started within subdir?
relpath = ".."
else:
relpath = "."
filename = relpath + "/build/colorlight_5a_75b/gateware/colorlight_5a_75b"
print(f"Load/flash {filename}.svf.flash ...")
load2flash(filename + ".svf.flash")
print("Done.")

@ -62,30 +62,6 @@ from neopixelengine import NeoPixelEngine
from helpers.prepare_firmware import copyjob
# ------------------------------ TEST -------------------------------
import sys
from migen import *
from migen.genlib.misc import WaitTimer
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.soc.interconnect.csr import *
from litex.build.generic_platform import *
from litex.boards.platforms import nexys_video as nexys
from litex.soc.interconnect import wishbone
from litex.soc.integration.soc_core import *
from litex.soc.integration.builder import *
from litex.soc.cores.uart import UARTWishboneBridge
from liteiclink.serwb.phy import SERWBPHY
from liteiclink.serwb.genphy import SERWBPHY as SERWBLowSpeedPHY
from liteiclink.serwb.core import SERWBCore
from litescope import LiteScopeAnalyzer
# ------------------------------- END --------------------------------
# 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 = [
@ -300,12 +276,7 @@ class _CRG(Module):
# BaseSoC ------------------------------------------------------------------------------------------
class BaseSoC(SoCCore):
#csr_map = { "npe_mem": 9 }
#csr_map.update(SoCCore.csr_map)
#mem_map = { "npe_mem": 0x02000000 }
#mem_map.update(SoCCore.mem_map)
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")
@ -363,33 +334,24 @@ class BaseSoC(SoCCore):
self.comb += platform.request("user_led_n").eq(counter[23]) # ~2Hz (?)
platform.add_extension(_gpios) # General LED outputs
#mem_map = { "logic_memory": 0x30000000 } # this just needs to be a unique block
#mem_map.update(SoCCore.mem_map)
self.submodules.npe = NeoPixelEngine(27)
self.add_csr("npe")
#self.mem_map["npe_mem"] = 0x02000000
#self.add_memory_region()
for i in range(42,56): # Do output on J4
self.comb += platform.request("gpio", i).eq(self.npe.bDataPin) # Output data pin
max_LEDS_per_chain = 256
self.submodules.npe1 = NeoPixelEngine(max_LEDS_per_chain)
self.submodules.npe2 = NeoPixelEngine(max_LEDS_per_chain)
self.submodules.npe3 = NeoPixelEngine(max_LEDS_per_chain)
self.submodules.npe4 = NeoPixelEngine(max_LEDS_per_chain)
self.add_csr("npe1")
self.add_csr("npe2")
self.add_csr("npe3")
self.add_csr("npe4")
self.comb += platform.request("gpio", 42).eq(self.npe1.bDataPin) # Output data pin
self.comb += platform.request("gpio", 43).eq(self.npe2.bDataPin) # Output data pin
self.comb += platform.request("gpio", 44).eq(self.npe3.bDataPin) # Output data pin
self.comb += platform.request("gpio", 45).eq(self.npe4.bDataPin) # Output data pin
#for i in range(42,56): # Do output on J4
# self.comb += platform.request("gpio", i).eq(self.npe.bDataPin) # Output data pin
# Build --------------------------------------------------------------------------------------------
def load(file):
#import os
#os.system("mkdir -p prog")
print("--------")
f = open("prog/openocd.cfg", "w")
f.write(
"""
interface vsllink
adapter_khz 100
reset_config none
jtag newtap ecp5 tap -irlen 8 -expected-id 0x41111043
""")
f.close()
os.system(f"openocd -f prog/openocd.cfg -c \"transport select jtag; init; svf {file}; exit\"")
exit()
def main():
parser = argparse.ArgumentParser(description="LiteX SoC on Colorlight 5A-75X")
@ -425,12 +387,6 @@ def main():
sdram_rate = args.sdram_rate,
**soc_core_argdict(args))
# 32MBit SPIFlash ---------------------------------------------------------------------------------
#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
@ -449,7 +405,8 @@ def main():
print(f"Executing ./bit_to_flash.py {name}.bit {name}.svf.flash")
from helpers.bit_to_flash import bit2svf
bit2svf(name + ".bit", name + ".svf.flash")
load(name + ".svf.flash")
from helpers.load_to_flash import load2flash
load2flash(name + ".svf.flash")
return
if __name__ == "__main__":

@ -1,419 +0,0 @@
#!/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
#
# 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.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 ...)
# 29.10.20/KQ Renamed to *_simple_72PCT as higher LED numbers (>192) consume to much space on FPGA
#
# 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.20 --server terminal --csr-csv build/csr.csv'
# You should see the LiteX BIOS and be able to interact with it
#
# ~/fpga/litex/litex/litex/soc/doc/module.py for autodoc functionality ...
#
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_simple_72PCT import NeoPixelEngine # Simple (naive) Version
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)
self.comb += platform.request("user_led_n").eq(counter[23]) # ~2Hz (?)
platform.add_extension(_gpios) # General LED outputs
self.submodules.npe = NeoPixelEngine(27)
self.add_csr("npe")
for i in range(42,56): # Do output on J4
self.comb += platform.request("gpio", i).eq(self.npe.bDataPin) # Output data pin
# Build --------------------------------------------------------------------------------------------
def load(file):
#import os
#os.system("mkdir -p prog")
print("--------")
f = open("prog/openocd.cfg", "w")
f.write(
"""
interface vsllink
adapter_khz 100
reset_config none
jtag newtap ecp5 tap -irlen 8 -expected-id 0x41111043
""")
f.close()
os.system(f"openocd -f prog/openocd.cfg -c \"transport select jtag; init; svf {file}; exit\"")
exit()
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))
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 bit2svf
bit2svf(name + ".bit", name + ".svf.flash")
load(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 ...

@ -58,11 +58,11 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
"""
def __init__(self, n_LEDs=3):
# On Colorlight-5A-75B/Lattice ECP5-25 (@i7/4th gen.):
# 256 NeoPixel LEDs will use 95% of TRELLIS_SLICES & REQUIRE ether_net & ether_bone being DISABLED! Calc. time >2h
# 192 NeoPixel LEDs w/ ethernet/etherbone will use 95% of TRELLIS_SLICES & take approx. 2:20h to calculate
# 27 NeoPixels LEDs w/ ethernet/etherbone used for any tests will require 73% of TRELLIS_SLICES & take less than 0:10h to calculate
#self.b24GRBArray = Array(Signal(24) for word24 in range(n_LEDs)) # Local 24-bit data Array
# 1x 256 NeoPixels Memory() option yields only 63%
# 2 pins with 256 NeoPixels each yields only 65%
# 4 pins with 256 NeoPixels each yields 94%
# 8 pins à 256 NeoPixels each yields 101% DP16KD -> fail!
# Inputs
self.b24Data2Load = CSRStorage(24, reset_less=True,
fields=[CSRField("Data2Load", size=24, description="*Field*: 24-Bit value")],
@ -96,38 +96,24 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
self.b8Offset = Signal(8) # Array rover
self.b24GRB = Signal(24) # Current 24-bit data to send
self.b12PulseLen = Signal(12) # Current pulse length
self.b5Count24 = Signal(5) # 24-Bit counter
# Wishbone r/w, logic ro
self.specials.mem = Memory(24, n_LEDs)
self.specials.rdport = self.mem.get_port()
self.comb += self.rdport.adr.eq(self.b8Offset)
self.b24GRBmem = Signal(24)
self.comb += self.b24GRBmem.eq(self.rdport.dat_r)
# No wishbone, logic r/w -> minimal footprint
#storage = Memory(24, n_LEDs) # Actual memory allocation
#self.w_address = Signal(8)
#self.w_data = Signal(24)
#self.r_address = Signal(8)
#self.r_data = Signal(24) # Current memory readout value
#wrport = storage.get_port(write_capable=True) # Write access (from logic only)
#self.specials += wrport
#self.comb += [
# #wrport.adr.eq(self.b8LoadOffset.storage),
# #wrport.dat_w.eq(self.b24Data2Load.storage),
# wrport.adr.eq(self.w_address),
# wrport.dat_w.eq(self.w_data),
# wrport.we.eq(1)
#]
#rdport = storage.get_port() # Read access (from logic only, not used)
#self.specials += rdport
#self.comb += [
# #rdport.adr.eq(self.b8Offset),
# #self.b24MemRead.eq(rdport.dat_r)
# rdport.adr.eq(self.r_address),
# self.r_data.eq(rdport.dat_r)
#]
self.b5Count24 = Signal(5) # 24-Bit counter
self.b24GRBmem = Signal(24) # Readout local store
storage = Memory(24, n_LEDs)
self.specials += storage
wrport = storage.get_port(write_capable=True) #, clock_domain="write")
self.specials += wrport
self.comb += [ # Write to memory
wrport.adr.eq(self.b8LoadOffset.storage),
wrport.dat_w.eq(self.b24Data2Load.storage),
wrport.we.eq(1)
]
rdport = storage.get_port()
self.specials += rdport
self.comb += [ # Read from memory
rdport.adr.eq(self.b8Offset),
self.b24GRBmem.eq(rdport.dat_r)
]
# Output
self.bDataPin = Signal() # To be wired to data pin ...
@ -136,14 +122,11 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
fsm = FSM(reset_state="IDLE") # FSM starts idling ...
self.submodules += fsm
#No write from logic now: self.sync += self.b24GRBArray[self.b8LoadOffset.storage].eq(self.b24Data2Load.storage) # Loader is allways active!
fsm.act("IDLE",
If((self.bEnable.storage==True) and (self.b8Len.storage > 0),
NextValue(self.b8Offset, 0), # Start @ 1st 24-bit data
#NextValue(self.b24GRB, self.b24GRBArray[0]), # ... but load 1st right away!
NextValue(self.b24GRB, self.b24GRBmem), # Depends upon b8Offset (cycle?)
NextValue(self.b5Count24,0), # Bit count 0..23
NextValue(self.b5Count24, 0), # Bit count 0..23
NextState("PREPAREBIT")
)
)
@ -205,7 +188,6 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
)
fsm.act("NEXTWORD",
If((self.b8Offset < self.b8Len.storage) & (self.bEnable.storage==True), # Still more words to come (& no exit request)?
#NextValue(self.b24GRB, self.b24GRBArray[self.b8Offset]), # Load in advance
NextValue(self.b24GRB, self.b24GRBmem), # Depends upon b8Offset!
NextState("PREPAREBIT")
).Else(
@ -235,12 +217,6 @@ def npe_testbench(npe):
yield
yield npe.b24Data2Load.storage.eq(0x000033)
yield
#yield npe.b24GRBArray[0].eq(0x123456)
#yield
#yield npe.b24GRBArray[1].eq(0x223344)
#yield
#yield npe.b24GRBArray[2].eq(0x654321)
#yield
yield npe.b8Len.storage.eq(3)
yield
yield npe.bEnable.storage.eq(True)

@ -1,215 +0,0 @@
#!/usr/bin/env python3
#
# neopixelengine_simple_72PCT.py
#
# NeoPixel protocol engine (wanna-be), see:
# http://www.adafruit.com/datasheets/WS2812.pdf
# https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
#
# History:
# --------
# 15.10.20/KQ Initial module export, test bench added
# 16.10.20/KQ Several submodules (incl. FSM) tested
# 17.10.20/KQ NeoPixel engine up & driving LEDs
# 18.10.20/KQ Cleanup & more 'array'
# 19.10.20/KQ Renamed from 'neopixelprotocol'
# 27.10.20/KQ Using AutoDoc etc(improved documentation generation)
# 29.10.20/KQ Renamed to *_simple_72PCT as TRELLIS_SLICE will be too high for higher LED numbers (>192)
#
from migen import *
from litex.soc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage, CSRField, CSRAccess
from litex.soc.integration.doc import AutoDoc, ModuleDoc
class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
"""
NeoPixelEngine class provides the protocol logic to drive NeoPixel LED strips
Usage:
######
#. Fill NeoPixelEngine's local array of GRB values (Green/Red/Blue).
Load ``b24Data2Load`` with a 24-bit (GRB) value.
Indicate the offset (where to store) via writing to ``b8LoadOffset``.
Repeat for all offsets 'til end of array ...
#. Indicate to NeoPixelEngine the actual no. of pixels used by setting up ``b8Len``.
#. Finally, enable processing by setting ``bEnable`` to true (1).
Inputs:
#######
:b24Data2Load: New data to be loaded (24 bits)
:b8LoadOffset: Offset (0..255) into b24GRBArray to load b24Data2Load to
:b8Len: Length (0..255) of actual 24-bit data entries (i.e. # of NeoPixels)
:bEnable: To enable running (after data preparation)
Output:
#######
:bDataPin: NeoPixel 'Din' pin output (wire to actual output pin ... ;)
"""
def __init__(self, n_LEDs=3):
# On Colorlight-5A-75B/Lattice ECP5-25 (@i7/4th gen.):
# 256 NeoPixel LEDs will use 95% of TRELLIS_SLICES & REQUIRE ether_net & ether_bone being DISABLED! Calc. time >2h
# 192 NeoPixel LEDs w/ ethernet/etherbone will use 95% of TRELLIS_SLICES & take approx. 2:20h to calculate
# 27 NeoPixels LEDs w/ ethernet/etherbone used for any tests will require 73% of TRELLIS_SLICES & take less than 0:10h to calculate
self.b24GRBArray = Array(Signal(24) for word24 in range(n_LEDs)) # Local 24-bit data Array
# Inputs
self.b24Data2Load = CSRStorage(24, reset_less=True,
fields=[CSRField("Data2Load", size=24, description="*Field*: 24-Bit value")],
description="""
Load value (24-Bit G/R/B).
Use ``b8LoadOffset`` first to indicate array location where to store this value.
""")
self.b8LoadOffset = CSRStorage(8, reset_less=True,
fields=[CSRField("LoadOffset", size=8, description="*Field*: 8-Bit value (0..max)")],
description="""
Offset into storage array for 24-bit G/R/B values.
Prepare this one first, then indicate value to store via ``b24Data2Load``.
""")
self.b8Len = CSRStorage(8, reset_less=True,
fields=[CSRField("Len", size=8, description="*Field*: 8-Bit value (0..max)")],
description="""
No. of active (GRB) entries.
Indicate actual # of elements used (may be less than max!)
""")
self.bEnable = CSRStorage(1, reset_less=True,
fields=[CSRField("Enable", size=1, description="*Field*: bit", values=[
("0", "DISABLED", "``NeoPixel`` protocol not active"),
("1", "ENABLED", "``NeoPixel`` protocol active"),
])
],
description="""
Enable free run (signal start & abort)
""")
# Local data
self.b8Offset = Signal(8) # Array rover
self.b24GRB = Signal(24) # Current 24-bit data to send
self.b12PulseLen = Signal(12) # Current pulse length
self.b5Count24 = Signal(5) # 24-Bit counter
# Output
self.bDataPin = Signal() # To be wired to data pin ...
###
fsm = FSM(reset_state="IDLE") # FSM starts idling ...
self.submodules += fsm
self.sync += self.b24GRBArray[self.b8LoadOffset.storage].eq(self.b24Data2Load.storage) # Loader is allways active!
fsm.act("IDLE",
If((self.bEnable.storage==True) and (self.b8Len.storage > 0),
NextValue(self.b8Offset, 0), # Start @ 1st 24-bit data
NextValue(self.b24GRB, self.b24GRBArray[0]), # ... but load 1st right away!
NextValue(self.b5Count24,0), # Bit count 0..23
NextState("PREPAREBIT")
)
)
# Protocol: T0H=400ns/T0L=850ns, T1H=800ns/T1L=450ns, RST>50µs(>50000ns)
fsm.act("PREPAREBIT",
If(self.b24GRB[23],
NextValue(self.b12PulseLen,47), # Compensate for 1 state changes w/o action ...),
NextState("T1H")
).Else(
NextValue(self.b12PulseLen,23), # Compensate for 1 state changes w/o action ...
NextState("T0H")
)
)
fsm.act("T1H",
NextValue(self.bDataPin, 1),
NextValue(self.b12PulseLen, self.b12PulseLen-1),
If(self.b12PulseLen == 0,
NextValue(self.b12PulseLen, 24), # Compensate for 3 state changes w/o action ...
NextState("T1L")
)
)
fsm.act("T1L",
NextValue(self.bDataPin, 0),
NextValue(self.b12PulseLen, self.b12PulseLen-1),
If(self.b12PulseLen == 0,
NextValue(self.b5Count24, self.b5Count24 + 1), # Next bit (of GRB)
NextValue(self.b24GRB, self.b24GRB << 1), # Next bit (of GRB)
NextState("NEXTBIT")
)
)
fsm.act("T0H",
NextValue(self.bDataPin, 1),
NextValue(self.b12PulseLen, self.b12PulseLen-1),
If(self.b12PulseLen == 0,
NextValue(self.b12PulseLen, 48), # Compensate for 3 state changes w/o action ...
NextState("T0L")
)
)
fsm.act("T0L",
NextValue(self.bDataPin, 0),
NextValue(self.b12PulseLen, self.b12PulseLen-1),
If(self.b12PulseLen == 0,
NextValue(self.b5Count24, self.b5Count24 + 1), # Next bit (of GRB)
NextValue(self.b24GRB, self.b24GRB << 1), # Next bit (of GRB)
NextState("NEXTBIT")
)
)
fsm.act("NEXTBIT",
If(self.b5Count24 < 24, # Not yet done?
NextState("PREPAREBIT")
).Else( # GRB word finished. More to come?
NextValue(self.b5Count24,0), # Bit count reset for next word
NextValue(self.b8Offset, self.b8Offset + 1), # Prepare offset for later use
NextState("NEXTWORD")
)
)
fsm.act("NEXTWORD",
If((self.b8Offset < self.b8Len.storage) & (self.bEnable.storage==True), # Still more words to come (& no exit request)?
NextValue(self.b24GRB, self.b24GRBArray[self.b8Offset]), # Load in advance
NextState("PREPAREBIT")
).Else(
NextValue(self.b12PulseLen, 4095), # >50µs required (3000 not ok!)
NextState("RST")
)
)
fsm.act("RST",
NextValue(self.bDataPin, 0),
NextValue(self.b12PulseLen, self.b12PulseLen-1),
If(self.b12PulseLen == 0,
NextState("IDLE")
)
)
def npe_testbench(npe):
print("----- npe testbench -----")
yield npe.b8LoadOffset.storage.eq(3)
yield
yield npe.b24Data2Load.storage.eq(0x100000)
yield
yield npe.b24GRBArray[0].eq(0x123456)
yield
yield npe.b24GRBArray[1].eq(0x223344)
yield
yield npe.b24GRBArray[2].eq(0x654321)
yield
yield npe.b8Len.storage.eq(3)
yield
yield npe.bEnable.storage.eq(True)
yield
#
for i in range(10000): # Send the whole data & restart ...
print(i,": ", sep="", end="")
print((yield npe.bDataPin)) # Actual pin to move
yield
if i == 5000:
yield npe.bEnable.storage.eq(True) # Enable quickest restart ...
yield
if __name__ == "__main__":
npe = NeoPixelEngine(n_LEDs=3)
run_simulation(npe, npe_testbench(npe), vcd_name="npe.vcd")

84059
npe.vcd

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save