|
|
|
@ -33,18 +33,24 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc): |
|
|
|
|
Usage: |
|
|
|
|
###### |
|
|
|
|
|
|
|
|
|
#. Fill NeoPixelEngine's local array of GRB values (Green/Red/Blue) |
|
|
|
|
1. Freeze operations by setting ``bEnable`` to false (0) |
|
|
|
|
|
|
|
|
|
2. 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 no. of DRAM addresses stored by writing to ``b6NoOfTables`` |
|
|
|
|
3. Indicate no. of DRAM addresses stored by writing the # to ``b6NoOfTables`` |
|
|
|
|
|
|
|
|
|
#. Indicate to NeoPixelEngine the actual no. of pixels used by setting up ``b9Len``. |
|
|
|
|
4. 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). |
|
|
|
|
5. Finally, enable processing by setting ``bEnable`` to true (1). |
|
|
|
|
|
|
|
|
|
6. NPE processing will now run async. to CPU until reset or s.a. (1.). |
|
|
|
|
CPU from now on will write to DRAM only (yet, L2 cache will have to be flushed), |
|
|
|
|
all data will be picked up by FPGA automatically ... |
|
|
|
|
|
|
|
|
|
Inputs: |
|
|
|
|
####### |
|
|
|
@ -107,8 +113,9 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc): |
|
|
|
|
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 (or 32-bit) counter |
|
|
|
|
|
|
|
|
|
self.b5Count24 = Signal(5) # 24-Bit (TODO: or 32-bit) counter |
|
|
|
|
self.b32BaseAddress = Signal(32) # Base DRAM address |
|
|
|
|
|
|
|
|
|
storage = Memory(32, n_TABLES) # * n_LEDs) |
|
|
|
|
self.specials += storage |
|
|
|
|
wrport = storage.get_port(write_capable=True) |
|
|
|
@ -121,43 +128,67 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc): |
|
|
|
|
rdport = storage.get_port() |
|
|
|
|
self.specials += rdport |
|
|
|
|
self.comb += [ # Read from DRAM addresses memory |
|
|
|
|
rdport.adr.eq(self.b6Table) # Index DRAM address array (READ) # TODO: Not used currently! |
|
|
|
|
rdport.adr.eq(self.b6Table) # Index DRAM address array (READ) |
|
|
|
|
] |
|
|
|
|
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 ... |
|
|
|
|
|
|
|
|
|
self.bDataPin = Array(Signal(1) for bit in range(64)) # To be wired to data pins ... |
|
|
|
|
|
|
|
|
|
### |
|
|
|
|
fsm = FSM(reset_state="IDLETABLE") # FSM starts idling ... |
|
|
|
|
self.submodules += fsm |
|
|
|
|
|
|
|
|
|
fsm.act("IDLETABLE", |
|
|
|
|
If((self.bEnable.storage==True) and (self.b9Len.storage > 0), |
|
|
|
|
NextValue(self.b6Table, 0), # Start @ 1st table |
|
|
|
|
NextValue(self.b6Table, 0), # Start @ 1st (DRAM) table data |
|
|
|
|
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") |
|
|
|
|
NextValue(self.b5Count24, 0), # Bit count 0..23 (TODO: or 0..31) |
|
|
|
|
NextState("TABLELOADLOOP") |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
fsm.act("TABLELOADLOOP", # 1st cycle delay for memory port access |
|
|
|
|
NextState("TABLELOAD2") |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
fsm.act("TABLELOAD2", # DRAM address now selected (avail.), hence pull it! |
|
|
|
|
NextValue(self.b32BaseAddress, rdport.dat_r), # Depends upon b6Table |
|
|
|
|
NextState("TABLELOAD3") |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
fsm.act("TABLELOAD3", # Load from DRAM address |
|
|
|
|
NextValue(dramtransfer.b32Address.storage, self.b32BaseAddress), |
|
|
|
|
NextState("TABLELOAD4") |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
fsm.act("TABLELOAD4", # Engage! |
|
|
|
|
NextValue(dramtransfer.bEnable.storage, 1), |
|
|
|
|
NextState("TABLELOADWAIT") |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
fsm.act("TABLELOADWAIT", # Wait for termination of transfer ... |
|
|
|
|
If(dramtransfer.bValid.storage, # Data avail.? |
|
|
|
|
NextValue(dramtransfer.bEnable.storage, 0), # Reset/ACK to DRAM transfer (sort of ...) |
|
|
|
|
NextState("GRBWORDLOOP") # Yap! |
|
|
|
|
) |
|
|
|
|
# TODO: Permit timeout indication ... |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
# G/R/B Word loop entry: |
|
|
|
|
fsm.act("IDLE1", # 1st cycle delay for memory port access |
|
|
|
|
fsm.act("GRBWORDLOOP", # 1st cycle delay for memory port access |
|
|
|
|
NextState("IDLE2") |
|
|
|
|
) |
|
|
|
|
fsm.act("IDLE2", # 2nd cycle delay ... |
|
|
|
|
NextState("IDLE3") |
|
|
|
|
) |
|
|
|
|
fsm.act("IDLE3", |
|
|
|
|
#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) |
|
|
|
|
fsm.act("IDLE3", |
|
|
|
|
NextValue(self.b32GRB, dramtransfer.b32Data.storage), # Depends upon b9Offset |
|
|
|
|
NextValue(self.b5Count24, 0), # Bit count 0..23 (TODO: or 0..31) |
|
|
|
|
NextState("PREPAREBIT") |
|
|
|
|
) |
|
|
|
|
# 24-bit or 32-bit loop entry: |
|
|
|
|
# 24-bit (TODO: or 32-bit) loop entry: |
|
|
|
|
# Protocol: T0H=400ns/T0L=850ns, T1H=800ns/T1L=450ns, RST>50µs(>50000ns) |
|
|
|
|
fsm.act("PREPAREBIT", |
|
|
|
|
If(self.b32GRB[23], |
|
|
|
@ -227,11 +258,9 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc): |
|
|
|
|
fsm.act("NEXTWORD2", |
|
|
|
|
NextState("NEXTWORD3") # Add one cycle for read port propagation! |
|
|
|
|
) |
|
|
|
|
fsm.act("NEXTWORD3", |
|
|
|
|
fsm.act("NEXTWORD3", |
|
|
|
|
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! |
|
|
|
|
NextValue(self.b32GRB, dramtransfer.b32Data.storage), # Depends upon b9Offset! |
|
|
|
|
NextState("PREPAREBIT") |
|
|
|
|
).Else( |
|
|
|
|
NextValue(self.b12PulseLen, 4095), # >50µs required (3000 not ok!) |
|
|
|
@ -249,15 +278,15 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc): |
|
|
|
|
|
|
|
|
|
fsm.act("NEXTTABLE", |
|
|
|
|
If(self.b6Table < self.b6NoOfTables.storage, |
|
|
|
|
NextValue(self.b9Offset, 0), # Start @ 1st 24-bit data (or 32-bit data) |
|
|
|
|
NextState("IDLE1") |
|
|
|
|
).Else( |
|
|
|
|
NextValue(self.b9Offset, 0), # Start @ 1st 24-bit data (TODO: or 32-bit data) |
|
|
|
|
NextState("TABLELOADLOOP") # Bring in next DRAM table |
|
|
|
|
).Else( # Final table: Restart! |
|
|
|
|
NextState("IDLETABLE") |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def npe_testbench(npe): |
|
|
|
|
def npe_testbench(npe): # TODO: Make it work again ... |
|
|
|
|
print("----- npe testbench -----") |
|
|
|
|
yield npe.b4LoadTable.storage.eq(0) |
|
|
|
|
yield |
|
|
|
|