Browse Source

Working DRAM data based (w/ one table ...)!

master
kaqu 3 months ago
parent
commit
29ea25083e
5 changed files with 94 additions and 78 deletions
  1. +7
    -3
      firmware/main.c
  2. +2
    -2
      neopixelar.py
  3. +74
    -63
      neopixelengine.py
  4. +11
    -7
      software/source/illumination.c
  5. +0
    -3
      software/source/main.c

+ 7
- 3
firmware/main.c View File

@ -73,10 +73,11 @@ extern void busy_wait(unsigned int ms); // Worx!
#define MAXTABLES 3 // 1..16 MUST match h/w!
#define MAXLEDS 27 // 1..256 MUST match h/w!
static int32_t arLEDBuffer[MAXTABLES][MAXLEDS]; // GRB values
#ifdef XXX
void enable_LEDS(int iEnable)
{
npe_b8Len_write(MAXLEDS); // Prepare length
npe_b6NoOfTables_write(MAXTABLES)
npe_b9Len_write(MAXLEDS); // Prepare length
npe_bEnable_write(iEnable ? 1 : 0); // Enable/disable
}
@ -177,6 +178,7 @@ int illumination(void)
enable_LEDS(0);
return 1;
}
#endif
//------------------- End of illumination demo --------------------------
int main(int i, char **c)
@ -300,8 +302,10 @@ int main(int i, char **c)
#if !defined(TERM_MINI) && !defined(TERM_NO_HIST)
hist_init();
#endif
#ifdef XXX
if(illumination()) // Initial demo / up indication
printf("Initial demo finished.\n");
printf("Initial demo finished.\n");
#endif
printf("\n%s", PROMPT);
#ifdef CONSOLE_NON_BLOCKING
if(!non_blocking)


+ 2
- 2
neopixelar.py View File

@ -396,13 +396,13 @@ class BaseSoC(SoCCore):
self.add_csr("mm2s")
self.submodules.sync_fifo = sync_fifo = SyncFIFO([("data", 32)], MAXWORDS, True)
self.comb += mm2s.source.connect(sync_fifo.sink) # Connect DMA-Reader.source -> FIFO.sink
self.submodules.dramtransfer = DRAMTransfer(maxwords=MAXWORDS, dma_reader=mm2s, sync_fifo=sync_fifo)
self.submodules.dramtransfer = dramtransfer = DRAMTransfer(maxwords=MAXWORDS, dma_reader=mm2s, sync_fifo=sync_fifo)
self.add_csr("dramtransfer")
# Adjust no. for your actual project ...
max_TABLES = 2 # 1..16
max_LEDS_per_chain = MAXWORDS # 1..256
self.submodules.npe = NeoPixelEngine(n_TABLES=max_TABLES, n_LEDs=max_LEDS_per_chain)
self.submodules.npe = NeoPixelEngine(n_TABLES=max_TABLES, n_LEDs=max_LEDS_per_chain, dramtransfer=dramtransfer)
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


+ 74
- 63
neopixelengine.py View File

@ -16,6 +16,7 @@
# 19.10.20/KQ Renamed from 'neopixelprotocol'
# 27.10.20/KQ Using AutoDoc etc(improved documentation generation)
# 29.10.20/KQ Memory object replaces array (due to resource consumption)
# 02.01.21/KQ DRAM loader integrated
#
from migen import *
@ -32,25 +33,29 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
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 ...
#. Fill NeoPixelEngine's local array of GRB values (Green/Red/Blue)
from DRAM location
Load ``b32DRAMAddress`` with a 32-bit DRAM memory pointer.
Indicate the offset (where to store) via writing to ``b6StoreOffset``.
Repeat for all DRAM address 'til end of array ...
#. Indicate to NeoPixelEngine the actual no. of pixels used by setting up ``b8Len``.
#. Indicate no. of DRAM addresses stored by writing to ``b6NoOfTables``
#. Indicate to NeoPixelEngine the actual no. of pixels used by setting up ``b9Len``.
For now, all arrays have to have the same max. length.
#. Finally, enable processing by setting ``bEnable`` to true (1).
Inputs:
#######
:b24Data2Load: New data to be loaded (24 bits)
:b32DRAMAddress: New DRAM address to be loaded (32 bits) into local memory
:b8LoadOffset: Offset (0..255) into b24GRBArray to load b24Data2Load to
:b6StoreOffset: Offset (0..63) into local memory to load b32DRAMAddress to
:b4LoadTable: Table index (0..15) where to load to via b8LoadOffset
:b6NoOfTables: No. of actually used tables (0..63)
:b8Len: Length (0..255) of actual 24-bit data entries (i.e. # of NeoPixels)
:b9Len: Length (0..511) of actual 32-bit data entries (i.e. # of NeoPixels)
:bEnable: To enable running (after data preparation)
@ -59,30 +64,29 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
:bDataPin: NeoPixel 'Din' pin output (wire to actual output pin ... ;)
"""
def __init__(self, n_TABLES=1, n_LEDs=3):
def __init__(self, n_TABLES=1, n_LEDs=3, dramtransfer=None):
# On Colorlight-5A-75B/Lattice ECP5-25 (@i7/4th gen.):
# 16 pins simultaneously driven (w/ 256 NeoPixels each) yield 94%
# Inputs
self.b24Data2Load = CSRStorage(24, reset_less=True,
fields=[CSRField("Data2Load", size=24, description="*Field*: 24-Bit value")],
self.b32DRAMAddress = CSRStorage(32, reset_less=True,
fields=[CSRField("Data2Load", size=32, description="*Field*: 32-Bit value")],
description="""
Load value (24-Bit G/R/B).
Use ``b8LoadOffset`` first to indicate array location where to store this value.
Load value (32-bit DRAM address).
Use ``b6StoreOffset`` first to indicate array location where to store new DRAM address value.
""")
self.b8LoadOffset = CSRStorage(8, reset_less=True,
fields=[CSRField("LoadOffset", size=8, description="*Field*: 8-Bit value (0..max)")],
self.b6StoreOffset = CSRStorage(6, reset_less=True,
fields=[CSRField("LoadOffset", size=6, description="*Field*: 6-Bit value (0..63)")],
description="""
Offset into storage array for 24-bit G/R/B values.
Prepare this one second, then indicate value to store via ``b24Data2Load``.
Offset into local memory for 32-bit DRAM address values.
Prepare this one second, then indicate value to store via ``b32DRAMAddress``.
""")
self.b4LoadTable = CSRStorage(4, reset_less=True,
fields=[CSRField("LoadTable", size=4, description="*Field*: 8-Bit value (0..max)")],
self.b6NoOfTables = CSRStorage(6, reset_less=True,
fields=[CSRField("NoOfTables", size=6, description="*Field*: 6-Bit value (0..63)")],
description="""
Table index into storage array for 24-bit G/R/B values.
Prepare this one first, then indicate offset value ``b8LoadOffset``.
No. of actually used different DRAM address blocks
""")
self.b8Len = CSRStorage(8, reset_less=True,
self.b9Len = CSRStorage(9, reset_less=True,
fields=[CSRField("Len", size=8, description="*Field*: 8-Bit value (0..max)")],
description="""
No. of active (GRB) entries.
@ -99,39 +103,42 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
""")
# Local data
self.b4Table = Signal(4) # Table rover
self.b8Offset = Signal(8) # Array rover
self.b24GRB = Signal(24) # Current 24-bit data to send
self.b6Table = Signal(6) # Table rover (0..63)
self.b9Offset = Signal(9) # Array rover (0..511)
self.b32GRB = Signal(32) # Current 24-bit(GRB) or 32-bit (GRBW) data to send
self.b12PulseLen = Signal(12) # Current pulse length
self.b5Count24 = Signal(5) # 24-Bit counter
self.b5Count24 = Signal(5) # 24-Bit (or 32-bit) counter
storage = Memory(24, n_TABLES * n_LEDs)
storage = Memory(32, n_TABLES) # * n_LEDs)
self.specials += storage
wrport = storage.get_port(write_capable=True)
self.specials += wrport
self.comb += [ # Write to memory
wrport.adr.eq((self.b4LoadTable.storage * n_LEDs) + self.b8LoadOffset.storage),
wrport.dat_w.eq(self.b24Data2Load.storage),
wrport.adr.eq(self.b6StoreOffset.storage), # Index local memory for WRITE
wrport.dat_w.eq(self.b32DRAMAddress.storage), # & store DRAM address
wrport.we.eq(1)
]
rdport = storage.get_port()
self.specials += rdport
self.comb += [ # Read from memory
rdport.adr.eq((self.b4Table * n_LEDs) + self.b8Offset)
self.comb += [ # Read from DRAM addresses memory
rdport.adr.eq(self.b6Table) # Index DRAM address array (READ) # TODO: Not used currently!
]
if dramtransfer != None: # This is for real!
self.sync += dramtransfer.b9Offset.storage.eq(self.b9Offset) # Index DRAM data value storage
# Output
self.bDataPin = Array(Signal(1) for bit in range(16)) # To be wired to data pins ...
# TODO: Expand to 60 outputs ...
###
fsm = FSM(reset_state="IDLETABLE") # FSM starts idling ...
self.submodules += fsm
fsm.act("IDLETABLE",
If((self.bEnable.storage==True) and (self.b8Len.storage > 0),
NextValue(self.b4Table, 0), # Start @ 1st table
NextValue(self.b8Offset, 0), # Start @ 1st 24-bit data (mem will be ready next cycle)
NextValue(self.b5Count24, 0), # Bit count 0..23
If((self.bEnable.storage==True) and (self.b9Len.storage > 0),
NextValue(self.b6Table, 0), # Start @ 1st table
NextValue(self.b9Offset, 0), # Start @ 1st 24-bit or 32-bit data (mem will be ready next cycle)
NextValue(self.b5Count24, 0), # Bit count 0..23 (or 0..31)
NextState("IDLE1")
)
)
@ -144,14 +151,16 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
NextState("IDLE3")
)
fsm.act("IDLE3",
NextValue(self.b24GRB, rdport.dat_r), # Depends upon b4Table/b8Offset
NextValue(self.b5Count24, 0), # Bit count 0..23
#NextValue(self.b32GRB, rdport.dat_r), # Depends upon b6Table/b9Offset
#if dramtransfer != None:
NextValue(self.b32GRB, dramtransfer.b32Data.storage), # Depends upon b9Offset
NextValue(self.b5Count24, 0), # Bit count 0..23 (or 0..31)
NextState("PREPAREBIT")
)
# 24-bit loop entry:
# 24-bit or 32-bit loop entry:
# Protocol: T0H=400ns/T0L=850ns, T1H=800ns/T1L=450ns, RST>50µs(>50000ns)
fsm.act("PREPAREBIT",
If(self.b24GRB[23],
If(self.b32GRB[23],
NextValue(self.b12PulseLen, 47), # Compensate for 1 state changes w/o action ...),
NextState("T1H")
).Else(
@ -160,10 +169,10 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
)
)
fsm.act("T1H",
NextValue(self.bDataPin[self.b4Table], 1),
NextValue(self.bDataPin[self.b6Table], 1),
NextValue(self.b12PulseLen, self.b12PulseLen - 1),
If(self.b12PulseLen == 0,
If(self.b5Count24 < 23, # Not final pulse of word
If(self.b5Count24 < 23, # Not final pulse of word (TODO: Adjust for 32-bit as well!)
NextValue(self.b12PulseLen, 24) # Compensate for 3 state changes w/o action ...
).Else( # Final word pulse special
NextValue(self.b12PulseLen, 21) # Compensate word load cycles
@ -172,20 +181,20 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
)
)
fsm.act("T1L",
NextValue(self.bDataPin[self.b4Table], 0),
NextValue(self.bDataPin[self.b6Table], 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)
NextValue(self.b32GRB, self.b32GRB << 1), # Next bit (of GRB)
NextState("NEXTBIT")
)
)
fsm.act("T0H",
NextValue(self.bDataPin[self.b4Table], 1),
NextValue(self.bDataPin[self.b6Table], 1),
NextValue(self.b12PulseLen, self.b12PulseLen - 1),
If(self.b12PulseLen == 0,
If(self.b5Count24 < 23, # Not final pulse of word?
If(self.b5Count24 < 23, # Not final pulse of word? (TODO: Adjust for 32-bit as well!)
NextValue(self.b12PulseLen, 48) # Compensate for 3 state changes w/o action ...
).Else( # Final word load special
NextValue(self.b12PulseLen, 45) # Compensate for load word cycles
@ -194,21 +203,21 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
)
)
fsm.act("T0L",
NextValue(self.bDataPin[self.b4Table], 0),
NextValue(self.bDataPin[self.b6Table], 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)
NextValue(self.b32GRB, self.b32GRB << 1), # Next bit (of GRB)
NextState("NEXTBIT")
)
)
fsm.act("NEXTBIT",
If(self.b5Count24 < 24, # Not yet done?
If(self.b5Count24 < 24, # Not yet done? (TODO: Adjust for 32-bit as well!)
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
NextValue(self.b9Offset, self.b9Offset + 1), # Prepare offset for later use
NextState("NEXTWORD1")
)
)
@ -219,8 +228,10 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
NextState("NEXTWORD3") # Add one cycle for read port propagation!
)
fsm.act("NEXTWORD3",
If((self.b8Offset < self.b8Len.storage) & (self.bEnable.storage==True), # Still more words to come (& no exit request)?
NextValue(self.b24GRB, rdport.dat_r), # Depends upon b4Table/b8Offset!
If((self.b9Offset < self.b9Len.storage) & (self.bEnable.storage==True), # Still more words to come (& no exit request)?
#NextValue(self.b32GRB, rdport.dat_r), # Depends upon b6Table/b9Offset!
#if dramtransfer != None:
NextValue(self.b32GRB, dramtransfer.b32Data.storage), # Depends upon b9Offset!
NextState("PREPAREBIT")
).Else(
NextValue(self.b12PulseLen, 4095), # >50µs required (3000 not ok!)
@ -228,17 +239,17 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
)
)
fsm.act("RST",
NextValue(self.bDataPin[self.b4Table], 0),
NextValue(self.bDataPin[self.b6Table], 0),
NextValue(self.b12PulseLen, self.b12PulseLen - 1),
If(self.b12PulseLen == 0,
NextValue(self.b4Table, self.b4Table + 1),
NextValue(self.b6Table, self.b6Table + 1),
NextState("NEXTTABLE")
)
)
fsm.act("NEXTTABLE",
If(self.b4Table < n_TABLES,
NextValue(self.b8Offset, 0), # Start @ 1st 24-bit data
If(self.b6Table < self.b6NoOfTables.storage,
NextValue(self.b9Offset, 0), # Start @ 1st 24-bit data (or 32-bit data)
NextState("IDLE1")
).Else(
NextState("IDLETABLE")
@ -250,19 +261,19 @@ def npe_testbench(npe):
print("----- npe testbench -----")
yield npe.b4LoadTable.storage.eq(0)
yield
yield npe.b8LoadOffset.storage.eq(0)
yield npe.b6LoadOffset.storage.eq(0)
yield
yield npe.b24Data2Load.storage.eq(0x110000)
yield npe.b32DRAMAddress.storage.eq(0x110000)
yield
yield npe.b8LoadOffset.storage.eq(1)
yield npe.b6LoadOffset.storage.eq(1)
yield
yield npe.b24Data2Load.storage.eq(0x002200)
yield npe.b32DRAMAddress.storage.eq(0x002200)
yield
yield npe.b8LoadOffset.storage.eq(2)
yield npe.b6LoadOffset.storage.eq(2)
yield
yield npe.b24Data2Load.storage.eq(0x000033)
yield npe.b32DRAMAddress.storage.eq(0x000033)
yield
yield npe.b8Len.storage.eq(3)
yield npe.b9Len.storage.eq(3)
yield
yield npe.bEnable.storage.eq(True)
yield


+ 11
- 7
software/source/illumination.c View File

@ -6,6 +6,7 @@
// --------
// 07.10.20/KQ Initial version
// 18.10.20/KQ RAM version w/ command interpreter loop ready
// 02.01.21/KQ Includes DRAM DMA data access now
//
#include <stdio.h>
@ -39,12 +40,13 @@
#include <liblitesdcard/sdcard.h>
#include "../include/dramtransfer.h"
#include "../include/illumination.h"
extern void busy_wait(unsigned int ms); // Worx!
#define MAXTABLES 3 // 1..16 MUST match h/w!
#define MAXLEDS 27 // 1..256 MUST match h/w!
static int32_t arLEDBuffer[MAXTABLES][MAXLEDS]; // GRB values
static int32_t arLEDBuffer[MAXTABLES][MAXLEDS] __attribute__((aligned(16)));; // GRB values
extern char kbhit(void);
extern int key_eval(void);
@ -68,19 +70,20 @@ int key_eval(void)
void enable_LEDS(int iEnable)
{
npe_b8Len_write(MAXLEDS); // Prepare length
npe_b6NoOfTables_write(MAXTABLES); // Prepare # of tables
npe_b9Len_write(MAXLEDS); // Prepare length
npe_bEnable_write(iEnable ? 1 : 0); // Enable/disable
}
void send_LEDs(void)
{
for(int j=0;j<MAXTABLES;j++) {
npe_b4LoadTable_write(j); // Select table
for(int i=0;i<MAXLEDS;i++) {
npe_b8LoadOffset_write(i); // and offset
npe_b24Data2Load_write(arLEDBuffer[j][i]); // store 32(24) bit value @Table/Offset
}
npe_b6StoreOffset_write(j); // Indicate which entry to use for address storage
npe_b32DRAMAddress_write((uint32_t)&arLEDBuffer[j]); // Base address of LED buffer
}
if(!load_FPGA((uint32_t)&arLEDBuffer[0], 0)) // Load <n> bytes from 0x40190000 ... (16 byte aligned!)
printf("*** send_LEDs(): load_FPGA() failed?!\n");
}
void clear_LEDs(int iTable)
@ -106,6 +109,7 @@ int illumination(void)
int iTable;
printf("----- Illumination demo -----\n");
// Prepare first output
iTable = 0;
load_triple_LEDs(iTable, green, red, blue); // 1st load


+ 0
- 3
software/source/main.c View File

@ -42,7 +42,6 @@
#include <liblitesdcard/sdcard.h>
#include "../include/systime.h"
#include "../include/dramtransfer.h"
#include "../include/illumination.h"
extern void busy_wait(unsigned int ms); // Worx!
@ -111,7 +110,6 @@ int main(int i, char **c)
printf("Press [w] slower, [s] faster, [x] to exit demo!\n");
while(1) {
dramtest(); // Test ...
if(illumination()) // The DEMO !
break;
}
@ -148,7 +146,6 @@ int main(int i, char **c)
cmd = command_dispatcher(command, nb_params, params);
if (!cmd) {
printf("Command not found?! Executing illumination demo instead ...\n");
dramtest(); // Test ...
illumination(); // more DEMO ...
}
}


Loading…
Cancel
Save