Page 1 of 1

SPI and DMA

Posted: Mon Mar 26, 2018 4:12 am
by Wolfe32
I'm trying to gain a better understanding of how the DMA engine works with SPI. To do this I'm working with the hardware registers directly. I've been successful at getting the SPI interface to work, but when I try to get DMA running it doesn't seem to start the data transfer. I've done some extensive searches and not come up with much other than some hits using the SDK which is so abstracted I can't seem to peel the layers back enough to see what's happening at the hardware level.

I'm working in the Arduino IDE.

I define some hardware addresses first, pulled from the Reference manual:

Code: Select all

#define DR_REG_SPI1_BASE                        0x3ff42000
#define DR_REG_SPI0_BASE                        0x3ff43000
#define DR_REG_SPI2_BASE                        0x3ff64000
#define DR_REG_SPI3_BASE                        0x3ff65000

#define SPI_DMA_CHAN_SEL                        0x3FF005A8

// See ESP32 Technical Reference Manual for register descriptions.
#define SPI_CMND                                0x00
#define SPI_ADDR                                0x01        // SPI_ADDR_REG + 0x04 bytes
#define SPI_CTRL                                0x02        // SPI_ADDR_REG + 0x08 bytes
#define SPI_CTRL1                               0x03        // SPI_ADDR_REG + 0x0C bytes
#define SPI_RD_STATUS                           0x04        // SPI_ADDR_REG + 0x10 bytes
#define SPI_CTRL2                               0x05        // SPI_ADDR_REG + 0x14 bytes
#define SPI_CLOCK                               0x06        // SPI_ADDR_REG + 0x18 bytes
#define SPI_USER                                0x07        // SPI_ADDR_REG + 0x1C bytes
#define SPI_USER1                               0x08        // SPI_ADDR_REG + 0x20 bytes
#define SPI_USER2                               0x09        // SPI_ADDR_REG + 0x24 bytes
#define SPI_MOSI_DLEN                           0x0A        // SPI_ADDR_REG + 0x28 bytes
#define SPI_MISO_DLEN                           0x0B        // SPI_ADDR_REG + 0x2C bytes
#define SPI_SLV_WRT_STATUS                      0x0C        // SPI_ADDR_REG + 0x30 bytes
#define SPI_PIN                                 0x0D        // SPI_ADDR_REG + 0x34 bytes
#define SPI_SLAVE                               0x0E        // SPI_ADDR_REG + 0x38 bytes
// 0x3C to 0x64 Slave registers.
#define SPI_DATA                                0x20        // SPI_DATA_REG + 0x80 bytes Data buffer is 16 words long.      
#define SPI_TX_CRC                              0x30        // SPI_ADDR_REG + 0xC0 bytes (extends for 256 bits)
#define SPI_EXT2                                0x3E        // SPI_ADDR_REG + 0xF8 bytes
#define SPI_DMA_CONF                            0x40        // SPI_ADDR_REG + 0x100 bytes
#define SPI_DMA_OUT_LINK                        0x41        // SPI_ADDR_REG + 0x104 bytes                                                            
#define SPI_DMA_IN_LINK                         0x42        // SPI_ADDR_REG + 0x108 bytes  
#define SPI_DMA_STATUS                          0x43        // SPI_ADDR_REG + 0x10C bytes  
#define SPI_DMA_INT_ENA                         0x44        // SPI_ADDR_REG + 0x110 bytes 
#define SPI_DMA_INT_RAW                         0x45        // SPI_ADDR_REG + 0x114 bytes  
#define SPI_DMA_INT_ST                          0x46        // SPI_ADDR_REG + 0x118 bytes  
#define SPI_DMA_INT_CLR                         0x47        // SPI_ADDR_REG + 0x11C bytes 
#define SPI_IN_ERR_EOF_DES_ADDR                 0x48        // SPI_ADDR_REG + 0x120 bytes 
#define SPI_IN_SUC_EOF_DES_ADDR                 0x49        // SPI_ADDR_REG + 0x124 bytes 
#define SPI_INLINK_DSCR                         0x4A        // SPI_ADDR_REG + 0x128 bytes 
#define SPI_INLINK_DSCR_BF0                     0x4B        // SPI_ADDR_REG + 0x12C bytes
#define SPI_INLINK_DSCR_BF1                     0x4C        // SPI_ADDR_REG + 0x130 bytes 
#define SPI_OUT_EOF_BFR_DES_ADDR                0x4D        // SPI_ADDR_REG + 0x134 bytes 
#define SPI_OUT_EOF_DES_ADDR                    0x4E        // SPI_ADDR_REG + 0x138 bytes 
#define SPI_OUTLINK_DSCR                        0x4F        // SPI_ADDR_REG + 0x13C bytes 
#define SPI_OUTLINK_DSCR_BF0                    0x50        // SPI_ADDR_REG + 0x140 bytes 
#define SPI_OUTLINK_DSCR_BF1                    0x51        // SPI_ADDR_REG + 0x144 bytes 
#define SPI_DMA_RSTATUS                         0x52        // SPI_ADDR_REG + 0x148 bytes 
#define SPI_DMA_TSTATUS                         0x53        // SPI_ADDR_REG + 0x14C bytes 

I then declare some pointers and buffers for DMA:

Code: Select all

volatile unsigned int *SPI2_Base_Ptr = (volatile unsigned int *)DR_REG_SPI2_BASE;
volatile unsigned int *SPI_DMA_CH_SLCT_Ptr = (volatile unsigned int *)SPI_DMA_CHAN_SEL;

unsigned int SPI2_DMA_Tx_LNK_LST[3] __attribute__((aligned(4))) __attribute__((section(".dram1")));     // Link list for SPI transmit DMA 
unsigned int SPI2_DMA_Rx_LNK_LST[3] __attribute__((aligned(4))) __attribute__((section(".dram1")));     // Link list for SPI receive DMA   
unsigned char SPI2_DMA_Tx_Buff[2560]  __attribute__((aligned(4))) __attribute__((section(".dram1")));         // Pointer to Tx DMA Buffer
unsigned char SPI2_DMA_Rx_Buff[2560]  __attribute__((aligned(4))) __attribute__((section(".dram1")));         // Pointer to Rx DMA Buffer    
Then I set up the link lists and DMA registers. I've been trying a few experiments so some of this might look a little strange. Note I'm attempting to use a single descriptor to send 2304 bytes. No linked descriptors yet:

Code: Select all

 //SPI2_Base_Ptr[SPI_DMA_CONF] = 0x00FF;                            // Reset DMA FIFO Pointers and AHB master -> Doesn't seem to matter
  SPI2_Base_Ptr[SPI_DMA_CONF] = 0x1900;                              // Reset DMA FIFO Pointers and AHB master
  SPI2_Base_Ptr[SPI_DMA_INT_CLR] = 0x1FF;                          // Clear any pending SPI DMA Interupts.

  // Set up link lists
  SPI2_DMA_Tx_LNK_LST[0] = (0xC0000000) | (2304<<12) | 2560;       // Link list for SPI transmit DMA. Owner=DMA, EOF, send 2304 bytes, buffer size 2560 bytes. SPI_MAX_DMA_LEN (4096-4)
  SPI2_DMA_Tx_LNK_LST[1] = (unsigned int)(SPI2_DMA_Tx_Buff);       // Address of buffer 
  SPI2_DMA_Tx_LNK_LST[2] = 0x00;                                                  // no more descriptors.
 
  SPI2_DMA_Rx_LNK_LST[0] = (0xC0000000) | (2304<<12) | 2560;     // Link list for SPI transmit DMA. Owner=DMA, EOF, send 2304 bytes, buffer size 2560 bytes. SPI_MAX_DMA_LEN (4096-4)
  SPI2_DMA_Rx_LNK_LST[1] = (unsigned int)(SPI2_DMA_Rx_Buff);       // Address of buffer 
  SPI2_DMA_Rx_LNK_LST[2] = 0x00;                                                  // no more descriptors.

  SPI_DMA_CH_SLCT_Ptr[0] = 0x04;                                                  // DMA Channel 1
  
  // Set up descriptor links
  SPI2_Base_Ptr[SPI_DMA_OUT_LINK] = (0x00000000) | ((unsigned int)(SPI2_DMA_Tx_LNK_LST) & 0xFFFFF); // Reset DMA FIFO Pointers and AHB master
  SPI2_Base_Ptr[SPI_DMA_IN_LINK] = (0x00000000) | ((unsigned int)(SPI2_DMA_Rx_LNK_LST) & 0xFFFFF);  // Reset DMA FIFO Pointers and AHB master

  SPI2_Base_Ptr[SPI_DATA] = 0xAA; // Put somethig in FIFO
  SPI2_Base_Ptr[SPI_MOSI_DLEN] = (2304*8)-1;
  SPI2_Base_Ptr[SPI_MISO_DLEN] = (2304*8)-1;

  //unsigned int* SPI2_DMA_Tx_LNK_LST_ptr = SPI2_DMA_Tx_LNK_LST;
  //unsigned int* SPI2_DMA_Rx_LNK_LST_ptr = SPI2_DMA_Rx_LNK_LST;

  SPI2_Base_Ptr[SPI_DMA_OUT_LINK] = (0x20000000) | ((unsigned int)(SPI2_DMA_Tx_LNK_LST) & 0xFFFFF); // Setup Out Link
  SPI2_Base_Ptr[SPI_DMA_IN_LINK] = (0x20000000) | ((unsigned int)(SPI2_DMA_Rx_LNK_LST) & 0xFFFFF);  // Setup In Link
And finally I initiate the transfer:

Code: Select all

SPI2_Base_Ptr[SPI_CMND] = 0x70000;  // Start Transfer
I've left out the code that sets up the SPI frequency, mode etc. That all seems to work fine when in non DMA mode.
I've printed out contents of the DMA registers to the terminal and they seem to be OK. I get a 0x03 in the DMA status register implying DMA read and write are enabled, but I don't see the descriptor address' in SPI_OUTLINK_DSCR and INLINK registers, and no data transfure although I get the correct number of clock cycles based on the values I set in SPI_MOSI_DLEN and SPI_MISO_DLEN.

Any clues greatly appreciated!!
Thanks,
Brian

Re: SPI and DMA

Posted: Sun Apr 01, 2018 4:12 pm
by Wolfe32
No feedback? Can a developer describe the sequence to kick off an SPI DMA transfer by describing the register setup? I just want to enable a single DMA transfer of arbitrary length with no interrupts. Unfortunately the hardware reference manual doesn't include enough detail.

Thanks,
Brian