Minimal footprint ready!

master
kaqu 2 years ago
parent 332a805a0c
commit b08b5a983a
  1. 28
      firmware/main.c
  2. 23
      neopixelar.py
  3. 68
      neopixelengine.py
  4. 84112
      npe.vcd

@ -70,33 +70,25 @@ static void boot_sequence(void)
//------------------- Illumination demo --------------------------------
extern void busy_wait(unsigned int ms);
#define MAXTABLES 16
#define MAXLEDS 27 // MUST match h/w!
static int32_t arLEDBuffer[MAXLEDS]; // GRB values
void enable_LEDS(int iEnable)
{
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
npe_b8Len_write(MAXLEDS); // Prepare length
npe_bEnable_write(iEnable?1:0); // Enable/disable
}
void send_LEDs()
{
for(int i=0;i<MAXLEDS;i++) {
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
}
for(int j=0;j<MAXTABLES;j++) {
npe_b4LoadTable_write(j);
for(int i=0;i<MAXLEDS;i++) {
npe_b8LoadOffset_write(i); // @Offset
npe_b24Data2Load_write(arLEDBuffer[i]); // store 32(24) bit value
}
}
busy_wait(25); // Minimum that meets the eye ;)
}

@ -335,22 +335,15 @@ class BaseSoC(SoCCore):
platform.add_extension(_gpios) # General LED outputs
max_TABLES = 16
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
self.submodules.npe = NeoPixelEngine(n_TABLES=max_TABLES, n_LEDs=max_LEDS_per_chain)
self.add_csr("npe")
#self.comb += platform.request("gpio", 42).eq(self.npe.bDataPin[0]) # Output data pin
#self.comb += platform.request("gpio", 43).eq(self.npe.bDataPin[1]) # Output data pin
for i in range(42,56+2): # Do output on J4 42/43/44/45/46/47/48/49/50/51/52/53/54/55
self.comb += platform.request("gpio", i).eq(self.npe.bDataPin[i-42]) # Output data pin
# Build --------------------------------------------------------------------------------------------
def main():

@ -47,6 +47,8 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
:b8LoadOffset: Offset (0..255) into b24GRBArray to load b24Data2Load to
:b4LoadTable: Table index (0..15) where to load to via b8LoadOffset
:b8Len: Length (0..255) of actual 24-bit data entries (i.e. # of NeoPixels)
:bEnable: To enable running (after data preparation)
@ -56,7 +58,7 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
:bDataPin: NeoPixel 'Din' pin output (wire to actual output pin ... ;)
"""
def __init__(self, n_LEDs=3):
def __init__(self, n_TABLES=1, n_LEDs=3):
# On Colorlight-5A-75B/Lattice ECP5-25 (@i7/4th gen.):
# 1x 256 NeoPixels Memory() option yields only 63%
# 2 pins with 256 NeoPixels each yields only 65%
@ -74,7 +76,13 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
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``.
Prepare this one second, then indicate value to store via ``b24Data2Load``.
""")
self.b4LoadTable = CSRStorage(4, reset_less=True,
fields=[CSRField("LoadTable", size=4, description="*Field*: 8-Bit value (0..max)")],
description="""
Table index into storage array for 24-bit G/R/B values.
Prepare this one first, then indicate offset value ``b8LoadOffset``.
""")
self.b8Len = CSRStorage(8, reset_less=True,
fields=[CSRField("Len", size=8, description="*Field*: 8-Bit value (0..max)")],
@ -93,13 +101,14 @@ 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.b12PulseLen = Signal(12) # Current pulse length
self.b5Count24 = Signal(5) # 24-Bit counter
self.b24GRBmem = Signal(24) # Readout local store
storage = Memory(24, n_LEDs)
storage = Memory(24, n_TABLES * n_LEDs)
self.specials += storage
wrport = storage.get_port(write_capable=True) #, clock_domain="write")
self.specials += wrport
@ -116,12 +125,19 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
]
# Output
self.bDataPin = Signal() # To be wired to data pin ...
self.bDataPin = Array(Signal(1) for bit in range(16)) # To be wired to data pins ...
###
fsm = FSM(reset_state="IDLE") # FSM starts idling ...
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
NextState("IDLE")
)
)
fsm.act("IDLE",
If((self.bEnable.storage==True) and (self.b8Len.storage > 0),
NextValue(self.b8Offset, 0), # Start @ 1st 24-bit data
@ -134,24 +150,24 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
# 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 ...),
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 ...
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),
NextValue(self.bDataPin[self.b4Table], 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),
NextValue(self.bDataPin[self.b4Table], 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)
@ -160,16 +176,16 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
)
fsm.act("T0H",
NextValue(self.bDataPin, 1),
NextValue(self.b12PulseLen, self.b12PulseLen-1),
NextValue(self.bDataPin[self.b4Table], 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),
NextValue(self.bDataPin[self.b4Table], 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)
@ -196,15 +212,27 @@ class NeoPixelEngine(Module, AutoCSR, AutoDoc, ModuleDoc):
)
)
fsm.act("RST",
NextValue(self.bDataPin, 0),
NextValue(self.b12PulseLen, self.b12PulseLen-1),
NextValue(self.bDataPin[self.b4Table], 0),
NextValue(self.b12PulseLen, self.b12PulseLen - 1),
If(self.b12PulseLen == 0,
NextValue(self.b4Table, self.b4Table + 1),
NextState("NEXTTABLE")
)
)
fsm.act("NEXTTABLE",
If(self.b4Table < 16,
NextState("IDLE")
).Else(
NextState("IDLETABLE")
)
)
def npe_testbench(npe):
print("----- npe testbench -----")
print("----- npe testbench -----")
yield npe.b4LoadTable.storage.eq(0)
yield
yield npe.b8LoadOffset.storage.eq(0)
yield
yield npe.b24Data2Load.storage.eq(0x110000)
@ -224,12 +252,12 @@ def npe_testbench(npe):
#
for i in range(10000): # Send the whole data & restart ...
print(i,": ", sep="", end="")
print((yield npe.bDataPin)) # Actual pin to move
print((yield npe.bDataPin[0])) # 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)
npe = NeoPixelEngine(n_TABLES=2, n_LEDs=3)
run_simulation(npe, npe_testbench(npe), vcd_name="npe.vcd")

84112
npe.vcd

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