Arduino Due as DMA SPI Slave to exchange 10 word array

Hi,
I have a setup containing Raspberry Pi3B as SPI master device connected with Simulink. It is sending an array of 16bit wide 10 numbers to an Arduino Due acting as slave device.

Datas sent to Raspberry Pi are not received properly, and are not in order. However, words received by Due is only in first iteration. The setup and configuration are perfectly working for a single word.

I am trying to impliment Arduino Due as DMA SPI Slave, so that the processor can be free. I need to exchange data uint16_t INPUT[10] & uint16_t OUTPUT[10].

Please tell me or provide any codes related to Due as DMA SPI Slave.
my current Slave configuration is:

void SPI0_Handler() {
  if(REG_SPI0_SR & SPI_SR_TDRE){    REG_SPI0_TDR = OutVal32[i]; }
  if(REG_SPI0_SR & SPI_SR_RDRF){    InVal32[i] = REG_SPI0_RDR;  }
    i++;
}

void slaveBegin(uint8_t _pin) {
  SPI.begin(_pin);
  REG_SPI0_CR = SPI_CR_SWRST;     // reset SPI
  REG_SPI0_CR = SPI_CR_SPIEN;     // enable SPI
  REG_SPI0_MR = 0x10;              // DLYBCS=0, PCS=0, MODFDIS=1, PS=0, MSTR=0(slave)
  REG_SPI0_CSR = 0x80;            // DLYBCT=0, DLYBS=0, SCBR=0, 16 bit transfer, Mode 1
  REG_SPI0_IDR = ~SPI_IDR_RDRF;
  REG_SPI0_IER = SPI_IDR_RDRF;
  NVIC_EnableIRQ(SPI0_IRQn);
}

Thanks and Regards,
Sourav

See TurboSpi library for Sam3x, it should help understand how AHB DMA works:

Thanks for reply and help.

As I am going through the codes, what I can understand is that,

  • This code example uses DMA of SPI in MASTER mode. Thus, there is no configuration available for the SS pin.
  • Also, I understand that, in Slave mode there is no need of the clock configuration.
  • Instead there should be an Interrupt handler for DMA receive request and also for End of transfer.

Correct me If I am wrong.
My prime concern is to operate this in DMA Slave mode. I have operated this in Normal SPI Slave mode, and after few transactions, the processor freezes. Thus DMA comes in picture. Hope It will solve my problem.
Thanks and Regards,
Sourav.

I went through all the codes in the TurboSPI library, and learned it is operating in master mode only.
In order to operate it in DMA SPI in SLAVE mode, I made a few modifications.
The code I modified is as follows:

#define USE_SAM3X_DMAC 1	/** Use SAM3X DMAC if nonzero */

#define USE_SAM3X_BUS_MATRIX_FIX 0	/** Use extra Bus Matrix arbitration fix if nonzero */

#define SAM3X_DMA_TIMEOUT 100	/** Time in ms for DMA receive timeout */

#define SPI_DMAC_RX_CH  1	/** DMAC receive channel */

#define SPI_DMAC_TX_CH  0	/** DMAC transmit channel */

#define SPI_TX_IDX  1		/** DMAC Channel HW Interface Number for SPI TX. */

#define SPI_RX_IDX  2		/** DMAC Channel HW Interface Number for SPI RX. */

//------------------------------------------------------------------------------
/** Disable DMA Controller. */
static void DMAC_Disable(){	
	DMAC->DMAC_EN &= (~DMAC_EN_ENABLE);	
}
/** Enable DMA Controller. */
static void DMAC_Enable(){	
	DMAC->DMAC_EN = DMAC_EN_ENABLE;		
}
/** Disable DMA Channel. */
static void DMAC_ChannelDisable(uint32_t ul_num){
	DMAC->DMAC_CHDR = DMAC_CHDR_DIS0 << ul_num;
}
/** Enable DMA Channel. */
static void DMAC_ChannelEnable(uint32_t ul_num){
	DMAC->DMAC_CHER = DMAC_CHER_ENA0 << ul_num;
}
/** Poll for transfer complete. */
static bool DMAC_ChannelTransferDone(uint32_t ul_num){
	return (DMAC->DMAC_CHSR & (DMAC_CHSR_ENA0 << ul_num)) ? false : true;
}

//------------------------------------------------------------------------------
// start TX-RX DMA exchange
static void SPI_DMA_TransReceive(const uint8_t * srcTX, uint8_t * dstRX, uint16_t count)  //uint16_t ??
{
	// start of TX DMA configuration
	static uint8_t ff = 0XFF;
	uint32_t srcTX_incr = DMAC_CTRLB_SRC_INCR_INCREMENTING;	// The source address is incremented
	/*
	if (!srcTX)
	{
		srcTX = &ff;
		srcTX_incr = DMAC_CTRLB_SRC_INCR_FIXED;	// The source address remains unchanged
	}*/
	DMAC_ChannelDisable(SPI_DMAC_TX_CH);
	DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)srcTX;
	DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DADDR = (uint32_t)&SPI0->SPI_TDR;
	DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_DSCR = 0;
	DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLA = 
		count |						// data amounut to transmit
		DMAC_CTRLA_SRC_WIDTH_HALF_WORD | 	// * the transfer size is set to 16-bit width
		DMAC_CTRLA_DST_WIDTH_HALF_WORD;	// * the transfer size is set to 16-bit width
		
	DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CTRLB = 
		DMAC_CTRLB_SRC_DSCR |			// brief (DMAC_CTRLB) Source Address Descriptor
		DMAC_CTRLB_DST_DSCR |			// brief (DMAC_CTRLB) Destination Address Descriptor 
		DMAC_CTRLB_FC_MEM2PER_DMA_FC |// Memory-to-Peripheral Transfer DMAC is flow controller
		srcTX_incr | 					// * Incremented source address
		DMAC_CTRLB_DST_INCR_FIXED;	// The destination address remains unchanged

	DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_CFG = 
		DMAC_CFG_DST_PER(SPI_TX_IDX) |	// Destination with Peripheral identifier (SPI_TX_IDX)
		DMAC_CFG_DST_H2SEL |  			// S/H.ware Handshaking Selection for the Destination
		DMAC_CFG_SOD | 				// Stop On Done
		DMAC_CFG_FIFOCFG_ALAP_CFG;	// The largest defined length AHB burst is performed 
									// on the destination AHB interface.
	// end of TX DMA configuration
	// start of RX DMA configuration
	DMAC_ChannelDisable(SPI_DMAC_RX_CH);
	DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_SADDR = (uint32_t)&SPI0->SPI_RDR; // source = Receive Data Reg
	DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DADDR = (uint32_t)dstRX;// * Destination = dst(memory)
	DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_DSCR = 0;// buffer transfer descriptor address
	
	DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLA = 
		count |						// data amounut to receive
		DMAC_CTRLA_SRC_WIDTH_HALF_WORD | 	// the transfer size is set to 16-bit width
		DMAC_CTRLA_DST_WIDTH_HALF_WORD;	// the transfer size is set to 16-bit width
		
	DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CTRLB = 
		DMAC_CTRLB_SRC_DSCR |			// brief (DMAC_CTRLB) Source Address Descriptor
		DMAC_CTRLB_DST_DSCR |			// brief (DMAC_CTRLB) Destination Address Descriptor 
		DMAC_CTRLB_FC_PER2MEM_DMA_FC |// Peripheral-to-Memory Transfer DMAC is flow controller
		DMAC_CTRLB_SRC_INCR_FIXED | 	// The source address remains unchanged
		DMAC_CTRLB_DST_INCR_INCREMENTING;//The destination address is incremented
		
	DMAC->DMAC_CH_NUM[SPI_DMAC_RX_CH].DMAC_CFG = 
		DMAC_CFG_SRC_PER(SPI_RX_IDX) |	// Source with Peripheral identifier (SPI_RX_IDX)
		DMAC_CFG_SRC_H2SEL | 			// S/H.ware Handshaking Selection for the Source
		DMAC_CFG_SOD | 				// Stop On Done
		DMAC_CFG_FIFOCFG_ASAP_CFG;	// When there is enough space/data available to perform-
									// -a single AHB access, then the request is serviced.
	// end of RX DMA configuration
	DMAC_ChannelEnable(SPI_DMAC_RX_CH);
	DMAC_ChannelEnable(SPI_DMAC_TX_CH);
}
//------------------------------------------------------------------------------
void TurboSPI::slaveBegin()
{
	// Configure the pins for SPI in peripheral mode
        PIO_Configure(
		g_APinDescription[PIN_SPI_MOSI].pPort,
		g_APinDescription[PIN_SPI_MOSI].ulPinType,
		g_APinDescription[PIN_SPI_MOSI].ulPin,
		g_APinDescription[PIN_SPI_MOSI].ulPinConfiguration);
	PIO_Configure(
		g_APinDescription[PIN_SPI_MISO].pPort,
		g_APinDescription[PIN_SPI_MISO].ulPinType,
		g_APinDescription[PIN_SPI_MISO].ulPin,
		g_APinDescription[PIN_SPI_MISO].ulPinConfiguration);
	PIO_Configure(
		g_APinDescription[PIN_SPI_SCK].pPort,
		g_APinDescription[PIN_SPI_SCK].ulPinType,
		g_APinDescription[PIN_SPI_SCK].ulPin,
		g_APinDescription[PIN_SPI_SCK].ulPinConfiguration);
	PIO_Configure(
		g_APinDescription[PIN_SPI_SS0].pPort,
		g_APinDescription[PIN_SPI_SS0].ulPinType,
		g_APinDescription[PIN_SPI_SS0].ulPin,
		g_APinDescription[PIN_SPI_SS0].ulPinConfiguration);	
	//pmc_enable_periph_clk(ID_SPI0);
#if USE_SAM3X_DMAC

	pmc_enable_periph_clk(ID_DMAC);
	DMAC_Disable();
	DMAC->DMAC_GCFG = DMAC_GCFG_ARB_CFG_FIXED;//Fixed priority arbiter
	DMAC_Enable();
	
#if USE_SAM3X_BUS_MATRIX_FIX	// not active here

	MATRIX->MATRIX_WPMR = 0x4d415400;
	MATRIX->MATRIX_MCFG[1] = 1;		//Single Access
	MATRIX->MATRIX_MCFG[2] = 1;		//Single Access
	MATRIX->MATRIX_SCFG[0] = 0x01000010;
	MATRIX->MATRIX_SCFG[1] = 0x01000010;
	MATRIX->MATRIX_SCFG[7] = 0x01000010;
	
#endif  // USE_SAM3X_BUS_MATRIX_FIX
#endif  // USE_SAM3X_DMAC
	slaveInit();	//  initialize SPI controller - Slave mode
}
//---------------------- Normal SPI init -----------------------------
//  initialize SPI controller - Slave mode
void TurboSPI::slaveInit()
{
	SPI.begin(PIN_SPI_SS0);
	SPI_Disable(SPI0);
	REG_SPI0_CR = SPI_CR_SWRST;        // reset SPI
	REG_SPI0_CR = SPI_CR_SPIEN;         // enable SPI
	//REG_SPI0_MR = SPI_MR_MODFDIS; // slave and no modefault
	REG_SPI0_MR = 0x10;                       // DLYBCS=0, PCS=0, MODFDIS=1, PS=0, MSTR=0(slave)
	REG_SPI0_CSR = 0x80;                     // DLYBCT=0, DLYBS=0, SCBR=0, 16 bit transfer, Mode 0/1
	REG_SPI0_IDR = ~SPI_IDR_RDRF;
	REG_SPI0_IER = SPI_IDR_RDRF;
	NVIC_EnableIRQ(SPI0_IRQn);
	SPI_Enable(SPI0);
}

Now, my first doubt is wheather it is required to include

void SPI0_Handler() {
  NVIC_DisableIRQ(SPI0_IRQn);

  // tasks... if any

  NVIC_EnableIRQ(SPI0_IRQn);
}

Or to just comment "NVIC_EnableIRQ(SPI0_IRQn);" in slaveInit()? and enable a DMA end of transfer Interrupt (how to do it?)

Most important, How SPI0 receive will invoke DMA transfer with uint16_t TXbuffer[10], RXbuffer[10] with pointing to the first value every time.
Do I need to write "DMAC->DMAC_CH_NUM[SPI_DMAC_TX_CH].DMAC_SADDR = (uint32_t)srcTX;" "somewhere"?
Please Help.
Thanks & Regards,
Sourav