Skip to content

Instantly share code, notes, and snippets.

@azechi
Created October 27, 2022 00:21
Show Gist options
  • Save azechi/aac55d88538a3427e71d78b08a8c291d to your computer and use it in GitHub Desktop.
Save azechi/aac55d88538a3427e71d78b08a8c291d to your computer and use it in GitHub Desktop.
MicroPython, Raspberry Pi Pico PIO, DMA, 74HC595, 8 LED Chaser
from uctypes import BF_POS, BF_LEN, BFUINT32, addressof, struct
# control register structure
DMA_CTRL_LAYOUT = {
"AHB_ERROR": 31<<BF_POS | 1<<BF_LEN | BFUINT32,
"READ_ERROR": 30<<BF_POS | 1<<BF_LEN | BFUINT32,
"WRITE_ERROR": 29<<BF_POS | 1<<BF_LEN | BFUINT32,
"BUSY": 24<<BF_POS | 1<<BF_LEN | BFUINT32,
"SNIFF_EN": 23<<BF_POS | 1<<BF_LEN | BFUINT32,
"BSWAP": 22<<BF_POS | 1<<BF_LEN | BFUINT32,
"IRQ_QUIET": 21<<BF_POS | 1<<BF_LEN | BFUINT32,
"TREQ_SEL": 15<<BF_POS | 6<<BF_LEN | BFUINT32,
"CHAIN_TO": 11<<BF_POS | 4<<BF_LEN | BFUINT32,
"RING_SEL": 10<<BF_POS | 1<<BF_LEN | BFUINT32,
"RING_SIZE": 6<<BF_POS | 4<<BF_LEN | BFUINT32,
"INCR_WRITE": 5<<BF_POS | 1<<BF_LEN | BFUINT32,
"INCR_READ": 4<<BF_POS | 1<<BF_LEN | BFUINT32,
"DATA_SIZE": 2<<BF_POS | 2<<BF_LEN | BFUINT32,
"HIGH_PRIORITY": 1<<BF_POS | 1<<BF_LEN | BFUINT32,
"EN": 0<<BF_POS | 1<<BF_LEN | BFUINT32
}
DMA_BASE = const(0x5000_0000)
# alias CSRs offset address
DMA_CSR_LENGTH = const(0x040)
DMA_0_READ_ADDR = const(0x000)
DMA_0_WRITE_ADDR = const(0x004)
DMA_0_TRANS_COUNT = const(0x008)
DMA_0_CTRL_TRIG = const(0x00c)
DMA_1_CTRL = const(0x010)
DMA_1_TRANS_COUNT_TRIG = const(0x01c)
DMA_3_READ_ADD_TRIG = const(0x03c)
# ... ch 0..11 (CH11_ALIAS_3_READ_ADD_TRIG = 0x2fc)
DMA_CHAN_ABORT = const(0x444)
# DMA_BASE + (DMA_DGB_LENGTH * ch) + DMA_DBG_(CTDREQ | TCR)
DMA_DBG_LENGTH = const(0x040)
DMA_DBG_CTDREQ = const(0x800)
DMA_DBG_TCR = const(0x804)
DREQ_PIO0_TX0 = const(0)
DREQ_PIO0_TX1 = const(1)
@micropython.viper
def print_dma(channel: uint):
csr_addr = uint(DMA_BASE) + (DMA_CSR_LENGTH * channel) + DMA_0_READ_ADDR
dbg_addr = uint(DMA_BASE) + (DMA_DBG_LENGTH * channel) + DMA_DBG_CTDREQ
csr = ptr32(csr_addr)
dbg = ptr32(dbg_addr)
ctrl = csr[3]
print(f'''\
DMA {channel=} CSRs 0x{csr_addr:04x} DBG 0x{dbg_addr:04x}
Read=0x{csr[0]:04x} Write=0x{csr[1]:04x}
Trans={uint(csr[2])}({uint(dbg[1])})
ERROR={ctrl>>29 & 0b111:03b} BUSY={ctrl>>24 & 1} EN={ctrl&0b1}
DREQ count={dbg[0] & 0b11_1111}
''')
@micropython.viper
def clear_dreq(channel: uint):
dbg = ptr32(uint(DMA_BASE) + (DMA_DBG_LENGTH * channel) + DMA_DBG_CTDREQ)
dbg[0] = 0b11_1111
@micropython.viper
def abort(channel: uint):
p = ptr32(DMA_BASE + DMA_CHAN_ABORT)
p[0] = (1 << channel)
while True:
if p[0] == 0:
return
@micropython.viper
def pause(channel: uint):
p = ptr32(uint(DMA_BASE) + (DMA_CSR_LENGTH * channel) + DMA_1_CTRL)
p[0] &= uint(0xFFFF_FFFE)
@micropython.viper
def unpause(channel:uint):
p = ptr32(uint(DMA_BASE) + (DMA_CSR_LENGTH * channel) + DMA_1_CTRL)
p[0] |= 1
from rp2 import PIO, asm_pio, StateMachine
from time import sleep_ms, sleep, sleep_us
from machine import Pin, mem32
from sys import byteorder
from uctypes import addressof, struct
import pio
import dma
@micropython.viper
def data():
data = bytes(10 * 4)
p = ptr32(data)
p[8] = uint(0xFFFF_FFFF)
p[9] = uint(0xAAAA_AAAA)
for i in range(8):
p[i] = ((1 << i) << 24)
return data
@micropython.viper
def data2():
data = bytes(10 * 4)
p = ptr32(data)
p[8] = uint(0xFFFF_FFFF)
p[9] = uint(0xAAAA_AAAA)
for i in range(8):
p[i] = ((1 << (7 - i)) << 24)
return data
@asm_pio(
set_init=(PIO.OUT_LOW, PIO.OUT_LOW)
)
def timer():
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
irq(4)
set(pins, 0b11)
set(x, 31) [5]
label("delay_high")
nop() [29]
jmp(x_dec, "delay_high")
# Cycles: 1 + 7 + 32 * (30 + 1) = 1000
set(pins, 0)
set(x, 31) [6]
label("delay_low")
nop() [29]
jmp(x_dec, "delay_low")
@asm_pio(
sideset_init=PIO.OUT_LOW,
out_init=PIO.OUT_LOW,
#fifo_join=PIO.JOIN_TX,
pull_thresh=8
)
def tx():
wait(1, irq, 4)
pull()
# CLK pulse width > 5 cycle
# SER setup > 6 cycle
out(pins, 1).side(0) [5]
label("loop")
nop().side(1)
out(pins, 1) [3]
jmp(not_osre, "loop").side(0) [4]
nop().side(1) [4]
led = Pin(25, Pin.OUT, value=0)
storage_clock = Pin(26, Pin.OUT, value=0)
serial_clock = Pin(15)
serial_input = Pin(14)
dma_ch_data = 9
dma_ch_config = 10
# clear dma channel state
dma.pause(dma_ch_data)
dma.abort(dma_ch_data)
dma.pause(dma_ch_config)
dma.abort(dma_ch_config)
timer = StateMachine(0, timer, freq=8000, set_base=led)
tx = StateMachine(1, tx, sideset_base=serial_clock, out_base=serial_input)
config = bytearray(4)
f = True
data = data()
data2 = data2()
def turn():
global config, f
config[:] = addressof(data if f else data2).to_bytes(4, byteorder)
f = not f
turn()
addr_dma_ch_data = dma.DMA_BASE + (dma.DMA_CSR_LENGTH * dma_ch_data)
addr_dma_ch_config = dma.DMA_BASE + (dma.DMA_CSR_LENGTH * dma_ch_config)
mem32[addr_dma_ch_config + dma.DMA_0_READ_ADDR] = addressof(config)
mem32[addr_dma_ch_config + dma.DMA_0_WRITE_ADDR] = addr_dma_ch_data + dma.DMA_3_READ_ADD_TRIG
mem32[addr_dma_ch_config + dma.DMA_0_TRANS_COUNT] = 1
ctrl = struct(addr_dma_ch_config + dma.DMA_1_CTRL, dma.DMA_CTRL_LAYOUT)
ctrl.TREQ_SEL = 0x3f # permanent request
ctrl.CHAIN_TO = dma_ch_data
ctrl.INCR_WRITE = 0
ctrl.INCR_READ = 0
ctrl.DATA_SIZE = 0x2 # SIZE_WORD: 4 bytes
ctrl.EN = 1
mem32[addr_dma_ch_data + dma.DMA_0_READ_ADDR] = addressof(data)
mem32[addr_dma_ch_data + dma.DMA_0_WRITE_ADDR] = pio.PIO0_BASE + (pio.PIO_TXF0 + 4)
mem32[addr_dma_ch_data + dma.DMA_0_TRANS_COUNT] = 8
ctrl = struct(addr_dma_ch_data + dma.DMA_0_CTRL_TRIG, dma.DMA_CTRL_LAYOUT)
ctrl.TREQ_SEL = dma.DREQ_PIO0_TX1
ctrl.CHAIN_TO = dma_ch_config
ctrl.INCR_WRITE = 0
ctrl.INCR_READ = 1
ctrl.DATA_SIZE = 0x2 # SIZE_WORD: 4 bytes
ctrl.EN = 1
tx.active(1)
timer.active(1)
PIO0_BASE = const(0x5020_0000)
PIO1_BASE = const(0x5030_0000)
PIO_CTRL = const(0x000)
PIO_FSTAT = const(0x004)
PIO_FDEBUG = const(0x008)
PIO_FLEVEL = const(0x00c)
# tx fifo0 ... tx fifo3
PIO_TXF0 = const(0x010)
# rx fifo0 ... rx firl3
PIO_RXF0 = const(0x020)
PIO_IRQ = const(0x030)
PIO_IRQ_FORCE = const(0x034)
# sm0 ... sm3
PIO_SM_CTRL_LENGTH = const(0x018)
PIO_SM0_CLKDIV = const(0x0c8)
PIO_SM0_EXECTRL = const(0x0cc)
PIO_SM0_SHIFTCTRL = const(0x0d0)
PIO_SM0_ADDR = const(0x0d4)
PIO_SM0_INSTR = const(0x0d8)
PIO_SM0_PINCTRL = const(0x0dc)
@micropython.viper
def print_sm(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
p = ptr32(base + PIO_CTRL)
irq = ptr32(base + PIO_IRQ)
smctrl = ptr32(base + (PIO_SM_CTRL_LENGTH * sm) + PIO_SM0_CLKDIV)
instr = smctrl[4] & 0xFFFF
print(f'''\
IRQ {(irq[0] >> 4) & 0xf:04b} {irq[0] & 0xf:04b}
PIO{pio}_SM{sm} enable={(p[0] >> sm) & 1} \
stalled={smctrl[1] >> 31}
WRAP TOP={smctrl[1] >> 12 & 0b1_1111} \
BOTTOM={smctrl[1] >> 7 & 0b1_1111} \
CURRENT={smctrl[3] & 0b1_1111}
INSTR=0x{instr:04x} {instr >> 13:03b} {instr>>8&0b1_1111:05b} {instr & 0xFF:08b}
TX LEVEL={(p[3] >> (sm * 8)) & 1} \
EMPTY={(p[1] >> 24 + sm) & 1} \
FULL={(p[1] >> 16 + sm) & 1} \
STALL={(p[2] >> 24 + sm) & 1} \
OVER={(p[2] >> 16 + sm) & 1}
RX LEVEL={(p[3] >> (sm * 8) + 4) & 1} \
EMPTY={(p[1] >> 8 + sm) & 1} \
FULL={(p[1] >> sm) & 1} \
UNDER={(p[2] >> 8 + sm) & 1} \
STALL={(p[2] >> sm) & 1}\
''')
@micropython.viper
def pio_sm(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
sm = uint(stateMachineNumber % 4)
return pio, sm
@micropython.viper
def restart(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
ctrl = ptr32(base + PIO_CTRL)
ctrl[0] = 1 << (4 + sm)
@micropython.viper
def pause(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
ctrl = ptr32(base + PIO_CTRL)
ctrl[0] &= (1 << sm) ^ 0b1111
@micropython.viper
def unpause(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
ctrl = ptr32(base + PIO_CTRL)
ctrl[0] |= 1 << sm
@micropython.viper
def clear_irq(pio: int, irq: int):
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
p = ptr32(base + PIO_IRQ)
p[0] = 1 << irq
@micropython.viper
def force_irq(pio: int, irq:int):
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
p = ptr32(base + PIO_IRQ_FORCE)
p[0] = 1 << irq
@micropython.viper
def clear_fifos(stateMachineNumber: int):
pio = 0 if stateMachineNumber < 4 else 1
base = uint(PIO0_BASE if pio == 0 else PIO1_BASE)
sm = uint(stateMachineNumber % 4)
shiftctrl = ptr32(base + (PIO_SM_CTRL_LENGTH * sm) + PIO_SM0_SHIFTCTRL)
shiftctrl[0] ^= uint(1 << 31)
shiftctrl[0] ^= uint(1 << 31)
@azechi
Copy link
Author

azechi commented Oct 27, 2022

unnamed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment