From 1dde87f58d58db986d6bb09e1bf142302182424b Mon Sep 17 00:00:00 2001 From: jimmy Date: Sat, 30 Dec 2023 13:41:48 +0000 Subject: [PATCH] Update dma.py --- dma.py | 262 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 209 insertions(+), 53 deletions(-) diff --git a/dma.py b/dma.py index 95ab17d..bec5c95 100644 --- a/dma.py +++ b/dma.py @@ -1,63 +1,219 @@ +from machine import Pin +from rp2 import PIO, StateMachine, asm_pio +from time import sleep +import array import uctypes -from uctypes import BF_POS, BF_LEN, BFUINT32 +from uctypes import BF_POS, BF_LEN, UINT32, BFUINT32, struct -DMA_CTRL_REG = { - "AHB_ERROR": 31 << BF_POS | 1 << BF_LEN | BFUINT32, - "READ_ERR": 30 << BF_POS | 1 << BF_LEN | BFUINT32, - "WRITE_ERR": 29 << BF_POS | 1 << BF_LEN | BFUINT32, - "Reserved": 25 << BF_POS | 4 << 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_PRIO": 1 << BF_POS | 1 << BF_LEN | BFUINT32, - "EN": 0 << BF_POS | 1 << BF_LEN | BFUINT32, +GPIO_BASE = 0x40014000 +GPIO_CHAN_WIDTH = 0x08 +GPIO_PIN_COUNT = 30 +PAD_BASE = 0x4001c000 +PAD_PIN_WIDTH = 0x04 +ADC_BASE = 0x4004c000 +PIO0_BASE = 0x50200000 +PIO1_BASE = 0x50300000 +DMA_BASE = 0x50000000 +DMA_CHAN_WIDTH = 0x40 +DMA_CHAN_COUNT = 12 + +DMA_SIZE_BYTE = 0x0 +DMA_SIZE_HALFWORD = 0x1 +DMA_SIZE_WORD = 0x2 + +# DMA: RP2040 datasheet 2.5.7 +DMA_CTRL_TRIG_FIELDS = { + "AHB_ERROR": 31<mem transfer -# dest, src can be bytearrays, size in bytes -def memcopy(ch, dest, src, size, enable=1): - ch.CTRL_TRIG.EN = 0 - ch.WRITE_ADDR = uctypes.addressof(dest) - ch.READ_ADDR = uctypes.addressof(src) - ch.TRANS_COUNT = size // (1 << ch.CTRL_TRIG.DATA_SIZE) - ch.CTRL_TRIG.EN = enable +DMA_CHANS = [struct(DMA_BASE + n*DMA_CHAN_WIDTH, DMA_CHAN_REGS) for n in range(0,DMA_CHAN_COUNT)] +DMA_DEVICE = struct(DMA_BASE, DMA_REGS) + +PIO0_TX0 = PIO0_BASE + 0x010 +PIO0_TX1 = PIO0_BASE + 0x014 +PIO0_TX2 = PIO0_BASE + 0x018 +PIO0_TX3 = PIO0_BASE + 0x01c +PIO1_TX0 = PIO1_BASE + 0x010 +PIO1_TX1 = PIO1_BASE + 0x014 +PIO1_TX2 = PIO1_BASE + 0x018 +PIO1_TX3 = PIO1_BASE + 0x01c + +GPIO_FUNC_SPI, GPIO_FUNC_UART, GPIO_FUNC_I2C = 1, 2, 3 +GPIO_FUNC_PWM, GPIO_FUNC_SIO, GPIO_FUNC_PIO0 = 4, 5, 6 +GPIO_FUNC_NULL = 0x1f + +DMA_CH0_AL3_TRANS_COUNT = DMA_BASE + 0x38 -# can be used to temporary construct the necessary DMA CTRL values -# then copy over to actual dma channel's CTRL value in one write -# e.g. -# scratch.CHAIN_TO=2; -# scratch.DATA_SIZE=2; -# ... -# CH0.CTRL_TRIG_RAW=scratch.CTRL_TRIG_RAW -tmp = bytearray(uctypes.sizeof(DMA_LAYOUT)) -scratch_ctrl = uctypes.struct(uctypes.addressof(tmp), DMA_CTRL_REG) +class PIO_DMA_Transfer(): + def __init__(self, dma_channel, sm_num, block_size, transfer_count): + self.dma_chan = DMA_CHANS[dma_channel] + self.channel_number = dma_channel + + if (sm_num == 0): + self.dma_chan.WRITE_ADDR_REG = PIO0_TX0 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO0_TX0 + elif (sm_num == 1): + self.dma_chan.WRITE_ADDR_REG = PIO0_TX1 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO0_TX1 + elif (sm_num == 2): + self.dma_chan.WRITE_ADDR_REG = PIO0_TX2 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO0_TX2 + elif (sm_num == 3): + self.dma_chan.WRITE_ADDR_REG = PIO0_TX3 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO0_TX3 + elif (sm_num == 4): + self.dma_chan.WRITE_ADDR_REG = PIO1_TX0 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO1_TX0 + elif (sm_num == 5): + self.dma_chan.WRITE_ADDR_REG = PIO1_TX1 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO1_TX1 + elif (sm_num == 6): + self.dma_chan.WRITE_ADDR_REG = PIO1_TX2 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO1_TX2 + elif (sm_num == 7): + self.dma_chan.WRITE_ADDR_REG = PIO1_TX3 + self.dma_chan.CTRL_TRIG.TREQ_SEL = DREQ_PIO1_TX3 + + if (block_size == 8): + self.dma_chan.CTRL_TRIG.DATA_SIZE = DMA_SIZE_BYTE + if (block_size == 16): + self.dma_chan.CTRL_TRIG.DATA_SIZE = DMA_SIZE_HALFWORD + if (block_size == 32): + self.dma_chan.CTRL_TRIG.DATA_SIZE = DMA_SIZE_WORD + + self.dma_chan.TRANS_COUNT_REG = transfer_count + + #Do I just always want these? + self.dma_chan.CTRL_TRIG.INCR_WRITE = 0 + self.dma_chan.CTRL_TRIG.INCR_READ = 1 + + def start_transfer(self, buffer): + self.dma_chan.READ_ADDR_REG = uctypes.addressof(buffer) + self.dma_chan.CTRL_TRIG.EN = 1 + + def transfer_count(self): + return self.dma_chan.TRANS_COUNT_REG + + def busy(self): + if self.dma_chan.CTRL_TRIG.DATA_SIZE == 1: + return True + else: + return False + + def abort_transfer(self): + pass + + def chain_to(self, channel): + self.dma_chan.CTRL_TRIG.CHAIN_TO = channel + + def get_number(self): + return self.channel_number + + + + +#looping transfers +#note -- see datasheet 2.5.7 +#location of registers is -- AL3 transcount / read address trigger. +#Writing to these (from one DMA channel) will re-trigger a second DMA channel +#need to set write ring. +#could also set read ring? +#out_buff = array.array('L', ((x if (x<1000) else (2000-x)) for x in range(NSAMPLES))) + +class DMA_Control_Block: + def __init__(self, this_chan, that_chan, read_address, transfer_count, loops): + self.dma_chan = DMA_CHANS[this_chan] + + #note -- need to set this up to get the right location + #but for now just always control channel 0 + self.dma_chan.WRITE_ADDR_REG = DMA_CH0_AL3_TRANS_COUNT + self.dma_chan.CTRL_TRIG.DATA_SIZE = DMA_SIZE_WORD + self.dma_chan.TRANS_COUNT_REG = 2 # two transfers. One is the count, one is the read_address. + #Then pauses until the other channel chains back to this. + + self.buffer = array.array('L', (x for x in range(2*loops))) + for x in range(loops): + self.buffer[2*x] = transfer_count + self.buffer[2*x+1] = read_address + + self.start_address = uctypes.addressof(self.buffer) + #set up read ring + that_chan.chain_to(this_chan) + + self.dma_chan.CTRL_TRIG.INCR_WRITE = 1 + self.dma_chan.CTRL_TRIG.INCR_READ = 1 + + self.dma_chan.CTRL_TRIG.RING_SEL = 1 + self.dma_chan.CTRL_TRIG.RING_SIZE = 3 # 1u<<3 bytes / 8 bytes + self.dma_chan.CTRL_TRIG.TREQ_SEL = 0x3f # unpaced transfer + + def start_chain(self): + self.dma_chan.READ_ADDR_REG = self.start_address + self.dma_chan.CTRL_TRIG.EN = 1 + + def transfer_count(self): + return self.dma_chan.TRANS_COUNT_REG + + def get_read_address(self): + return self.dma_chan.READ_ADDR_REG + + def busy(self): + if self.dma_chan.CTRL_TRIG.DATA_SIZE == 1: + return True + else: + return False +