# first attempt at DMA channel arithmetic # datasheet # https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf # ==================================== # imports import array from machine import mem32 from ctypes import addressof #==== DMA ============================ # register adddresses for DMA # datasheet page 102 DMA_base = 0x50000000 # === DMA0 regs # starting read address DMA_RD_ADDR0 = 0x000 + DMA_base # sets start adddr, then triggers channel DMA_RD_ADDR0_trigger = 0x03c + DMA_base # starting write address DMA_WR_ADDR0 = 0x004 + DMA_base # number off ITEMS (not bytes to trnasffer) DMA_TR_CNT0 = 0x008 + DMA_base # Channel control -- many fields -- see below DMA_CTRL0 = 0x00c + DMA_base # === DMA1 regs # starting read address DMA_RD_ADDR1 = 0x040 + DMA_base # starting write address DMA_WR_ADDR1 = 0x044 + DMA_base # number off ITEMS (not bytes to trnasffer) DMA_TR_CNT1 = 0x048 + DMA_base # Channel control -- many fields -- see below DMA_CTRL1 = 0x04c + DMA_base # === Kill channels # kill the channels by writing one to the bit number of thh channel DMA_Ch_ABORT = 0x444 + DMA_base # pacing timer The pacing timer produces TREQ assertions # at a rate set by ((X/Y) * sys_clk)<1 # Where X is top 16 bits, Y the bottom 16 bits DMA_TIMER0 = 0x420 + DMA_base # sniff control -- side channel for arithmetic during DMA DMA_SNIFF_CTRL = 0x434 + DMA_base # sniff data -- result of side channel aarithmetic # for many i/o blocks there are XOR, SET, CLR registers # aat offsets of 0x1000 (manual 2.1.2. Atomic Register Access) # For two logic bits A and B: # load B then SET bits using A as a mask implements OR # load B then CLR bits using NOT(A) as a mask implements AND DMA_SNIFF_DATA = 0x438 + DMA_base DMA_SNIFF_DATA_XOR = 0x1438 + DMA_base DMA_SNIFF_DATA_SET = 0x2438 + DMA_base DMA_SNIFF_DATA_CLR = 0x3438 + DMA_base #========================================= # === bits in /DMA ctrl register # busy during transfer DMA_BUSY = (1<<24) # bit 24 high when busy # sniff data enable DMA_CH_SNIFF_EN = (1<<23) # turn off channel done IRQ DMA_IRQ_QUIET = (1<<21) # bit 21 turn off interrupt # Triggger Source table page 96 section 2.5.3.1 # More tirgger info page 114 # 0x0 to 0x3a → select DREQ n as TREQ from table above # 0x3b → Select Timer 0 as TREQ # 0x3c → Select Timer 1 as TREQ # 0x3d → Select Timer 2 as TREQ (Optional) # 0x3e → Select Timer 3 as TREQ (Optional) # 0x3f → Permanent request, for unpaced transfers. # bits 15:20 trigger request source def DMA_TREQ(trigger_source): return (trigger_source & 0x3f)<<15 # When this channel completes, it will trigger the channel # indicated by CHAIN_TO. Disable by setting CHAIN_TO = # (this channel). def DMA_CHAIN_TO (next_ch): return (next_ch & 0x0f)<<11 # bits 11:14 next chnnel # # Select whether RING_SIZE applies to read or writeaddresses. # If 0, read addresses are wrapped on a (1 << RING_SIZE) # boundary. If 1, write addresses are wrapped. DMA_RING_SEL = (1<<10) # bits 10 -- loop on write addr # If 1, write addresses are wrapped. # Ring sizes between 2 and 32768 BYTES are possible. This # can apply to either read or write addresses, based on # value of RING_SEL. NOTE BYTES, not words # 0x0 → RING_NONE def DMA_RING_SIZE(ring_size): return (ring_size & 0x0f)<<6 # bits 6:9 # increment or keep constant wrrite nd read addr # useful for peripheril write/read DMA_WR_INC = (1<<5) # bits 5 DMA_RD_INC = (1<<4) # bits 4 # Set the size of each bus transfer (byte/halfword/word). # READ_ADDR and WRITE_ADDR advance by this amount # (1/2/4 bytes) with each transfer. # 0x0 → SIZE_BYTE # 0x1 → SIZE_HALFWORD # 0x2 → SIZE_WORD def DMA_DATA_WIDTH(data_width): return (data_width & 0x03)<<2 # bits 2:3 # give this channel more access if several channels aare on DMA_HIGH_PRI = (1<<1) # bit 1 # turn on the channel DMA_EN = 1 # bits 0 #========================================= # === bits in SNIFF ctrl register # DMA_SNIFF_CTRL # invert bits when read DMA_OUT_INV = (1<<11) # bit-reverse when read DMA_OUT_REV = (1<<10) # operation performed # op 0 -- CRC32 # op 1 -- CRC32 bit revv # op 2 -- CRC16 CCITT # op 3 -- CRC16 CCITT bit rev # ...??? # op 0x0e -- XOR reduction to 1 bit # op 0x0f -- 32-bit cumulaative add def DMA_CALC(function): return (function & 0x0f)<<5 # bits 5:8 # channe to sniff def DMA_DMACH(ch_num): return (ch_num & 0x0f)<<1 # bits 1:4 # enable sniff DMA_SNIFF_EN = 1 # ==================================== # data structures input0 = 20 input1 = -10 input2 = 5 # input data test_mem = array.array('i', [input0, input1, input2]) # DMA target -- bit_bucket = array.array('i', [0])# discard actual transfer # pointers to the two structures pt_test_mem = addressof(test_mem) pt_bit_bucket = addressof(bit_bucket) # ==================================== # set up sniffer sniff_add = 0x0f chan_num = 0 mem32[DMA_SNIFF_CTRL] = (DMA_CALC(sniff_add) | DMA_DMACH(chan_num) | #DMA_OUT_INV | DMA_SNIFF_EN ) # numbers are added to value in sniff daata mem32[DMA_SNIFF_DATA_CLR] = 0xffff_ffff mem32[DMA_SNIFF_DATA_SET] = 0x0001 mem32[DMA_SNIFF_DATA_XOR] = 0x0007 # net result is to set register to 6 intial_sum = mem32[DMA_SNIFF_DATA] # ==================================== # === Channel 0 test add mem32[DMA_RD_ADDR0] = pt_test_mem mem32[DMA_WR_ADDR0] = pt_bit_bucket # discard mem32[DMA_TR_CNT0] = 3 # three inputs # now the conntrol registerr data_32 = 0x02 # 32 bit persist = 0x3f # set up control and start the DMA mem32[DMA_CTRL0] = (DMA_IRQ_QUIET | DMA_CH_SNIFF_EN | DMA_TREQ(persist) | DMA_RD_INC | #DMA_WR_INC | DMA_DATA_WIDTH(data_32) | DMA_EN ) # =================================== #kill channel 0 and 1 DMA_CH_ABORT = 0x0f # set bit 0 to kill ch0 and ch1 ## # print result print('inputs =', test_mem) print('sum(inputs) +',intial_sum,'=', mem32[DMA_SNIFF_DATA]) print('bit_bucket =', bit_bucket) ## end