XDMA and circular buffer

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I have data that come to my SAME70 via SPI. I receive them with the XDMAC 6 bytes by 6 bytes, and then in the End of Block interrupt, update the destination address and re-configure the XDMAC. This works, but I have to configure SPI sender to have a quite big blank time between 2 SPI data, if I don't want to loose data. This is because of the time to reconfigure the XDMAC at the end of the 6 bytes.

 

What I'd like to do to improve that is to have some sort of circular buffer to receive data. This mean, configure XDMAC to get 256 bytes, and when it is at the end, automatically restart without having to manually reconfigure it. Then in my main loop, I'll look at the XDMAC_CDA register to know if there are data to treat.

 

So 2 questions:

 - Is it the best way to go?

 - the XDMAC part of the datasheet isn't ver detailed, and I have difficulties to understand how to do that. I guess it has something to do with linked list, but I'm not sure how to do that using ASF.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which APIs have you used to update the destination address and re-configure the XDMAC ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the XDMAC BIS handler, I just call something like that:
 

xdmac_channel_disable(XDMAC, SPI_XDMAC_RX_CH);
index_spi_rx_buffer += 6;
spi_xdmac_rx_cfg.mbr_da = (uint32_t)&(spi_rx_buffer[index_spi_rx_buffer]);
xdmac_configure_transfer(XDMAC, SPI_XDMAC_RX_CH, &spi_xdmac_rx_cfg);
xdmac_channel_enable(XDMAC, SPI_XDMAC_RX_CH);

 

The problem is while I wait for the interrupt handler to be called and those few lines to be executed, I loose data if SPI data are sent too fast.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've implemented a two-block linked list to solve this issue (uart transmission).

I've modified the _configureRxDma function to point always on the same buffer.

Here, the code:

 /* Setup RX Link List */
  if (pUsartRx->dmaProgrammingMode == XDMAD_LLI)
  {  
    LLI_Size = pUsartRx->dmaBlockSize;
    pBuff = pUsartRx->pBuff;
    
    if (pUsartRx->pLLIview != NULL)
    {
      free(pUsartRx->pLLIview);
      pUsartRx->pLLIview = NULL;
    }
    
    pUsartRx->pLLIview = malloc(sizeof(LinkedListDescriporView1) * LLI_Size);
    
    if (pUsartRx->pLLIview == NULL)
    {
      TRACE_ERROR(" Can not allocate memory to Rx LLI");
      return USARTD_ERROR;
    }
    
    xdmadRxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN |
                         XDMAC_CC_MBSIZE_SIXTEEN |
                         XDMAC_CC_DSYNC_PER2MEM |
                         XDMAC_CC_MEMSET_NORMAL_MODE |
                         XDMAC_CC_CSIZE_CHK_1 |
                         XDMAC_CC_DWIDTH_BYTE |
                         XDMAC_CC_SIF_AHB_IF1 |
                         XDMAC_CC_DIF_AHB_IF1 |
                         XDMAC_CC_SAM_FIXED_AM |
                         XDMAC_CC_DAM_INCREMENTED_AM |
                         XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber
                         (pUsart->usartId, XDMAD_TRANSFER_RX));
    xdmadRxCfg.mbr_bc = 0;
    
    for (i = 0; i < LLI_Size; i++)
    {
      pUsartRx->pLLIview[i].mbr_ubc = XDMA_UBC_NVIEW_NDV1 |
                                      XDMA_UBC_NSEN_UNCHANGED |
                                      XDMA_UBC_NDEN_UPDATED |
                                      ((i == LLI_Size - 1) ? 
                                       ((pUsartRx->dmaRingBuffer) ? XDMA_UBC_NDE_FETCH_EN : 0) :
                                       XDMA_UBC_NDE_FETCH_EN) |
                                      pUsartRx->BuffSize;
      
      pUsartRx->pLLIview[i].mbr_sa = (uint32_t)&pUsartHwRx->US_RHR;
      pUsartRx->pLLIview[i].mbr_da = (uint32_t)pBuff;
      pUsartRx->pLLIview[i].mbr_nda = (i == (LLI_Size - 1)) ?
        ((pUsartRx->dmaRingBuffer) ? (uint32_t)pUsartRx->pLLIview : 0)
          : (uint32_t)&pUsartRx->pLLIview[ i + 1 ];
    }
    
    SCB_CleanDCache_by_Addr((uint32_t *)(pUsartRx->pLLIview),
                            sizeof(LinkedListDescriporView1)*LLI_Size);
    
    xdmaCndc = XDMAC_CNDC_NDVIEW_NDV1 |
               XDMAC_CNDC_NDE_DSCR_FETCH_EN |
               XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
               XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED;
    xdmaInt = ((pUsartRx->dmaRingBuffer) ? XDMAC_CIE_BIE : XDMAC_CIE_LIE);
  } else
    return 1;
  
  if (XDMAD_ConfigureTransfer(
                              pXdmadRx, pUsartRx->ChNum, &xdmadRxCfg, xdmaCndc,
                              (uint32_t)pUsartRx->pLLIview, xdmaInt))
    return USARTD_ERROR;
  
  return 0;
     usart_rx_channel.dmaProgrammingMode = XDMAD_LLI;
     usart_rx_channel.dmaBlockSize = 2;
     usart_rx_channel.dmaRingBuffer = 1;

Hope it helps you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

Thanks for sharing this code. Yes that help a lot understanding the way to do things, and I'll try that very soon.

Just one question to be completely sure: am I right to think that your XDMAD_ConfigTransfer fills:

  • CSA register with pLLIview[0].mbr_sa value
  • CDA register with pLLIview[0].mbr_da value
  • CNDA register with pLLIview[0].mbr_nda
  • CNDC register with xdmaCndc value
  • CUBC register with pLLIview[0].mbr_ubc value
  • CBC register with xdmadRxCfg.mbr_bc value
  • CC register with xdmadRxCfg.mbr_cfg value
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got everything working. I can speed up my SPI connexion by a factor of 10, that's very nice! Thanks again.

 

The only thing I faced is this one:

I setup my circular buffer and it runs continuously.

In my main loop, I check if there are data in the buffer by looking at the XDMAC_CDA register and compare it to the previous value.

The problem I faced is that the XDMAC_CDA register only increase by 4 where we get 4 8bits data. So until I receive 4 bytes, data are not transferred into the RAM and I can't treat them.

 

That's strange because I configure the XDMA channel with XDMAC_CC_MBSIZE_SINGLE and XDMAC_CC_DWIDTH_BYTE. I thought that every byte would be transfered instantly and XDMAC_CDA register would be increased by 1.

 

I see in your code that you used XDMAC_CC_MBSIZE_SIXTEEN. Does this mean that data are transferred to the RAM every 16*4 = 64 bytes?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

Have you solved your problem?

I tried   with    XDMAC_SoftwareFlushReq before reading / memory_barrier(), and seems ok

 

Yannis

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I "solved" it by adding 2 dummy bytes to my frames to it reach 8 bytes. I didn't think about this flush register. Thanks for the hint, I'll play with that part again to test that.