4

Due to some design requirement, I need to change DMA descriptor at runtime. To achieve this, I am following below steps:

  1. Abort DMA channel. DMA hardware will then save currently executing descriptor at write_back RAM location of same DMA channel.
  2. Wait until Abort Completed
  3. Modify DMA descriptor on write_back RAM location.
  4. Enable DMA channel again

This is code snippet I am using:

//Select DMA channel
DMAC->CHID.reg = DMAC_CHID_ID(cSPIDMAResource0.channel_id);

//Abort Selected DMA channel
DMAC->CHCTRLA.reg &= ~DMA_CHANNEL_ENABLE_BIT_POS;

//Wait until Abort completed
while((DMAC->CHCTRLA.reg & DMA_CHANNEL_ENABLE_BIT_POS) == DMA_CHANNEL_ENABLE_BIT_POS);

/*
    Modify Descriptor here 
*/

//Enable DMA channel
DMAC->CHCTRLA.reg |= DMA_CHANNEL_ENABLE_BIT_POS;

Above mentioned steps work fine without any problem, But I am facing Descriptor corruption issue during long run.

DMA hardware is storing currently executing descriptor at the write_back RAM location of another DMA channel (instead of own write_back RAM location) when DMA abort is performed.

If anyone has any idea of what went wrong, or has an idea of how I can avoid the Descriptor corruption issue completely, I would like to try it out.

GOKU
  • 221
  • 2
  • 6
  • Possibly the same issue as https://stackoverflow.com/questions/62833342/atmel-samd21-dmac-writeback-failure – Jeremy May 10 '23 at 08:08

1 Answers1

1

Why not use the atmel software framework's dma driver? Here's how they do the abort.

void dma_abort_job(struct dma_resource *resource)
{
    uint32_t write_size;
    uint32_t total_size;

    Assert(resource);
    Assert(resource->channel_id != DMA_INVALID_CHANNEL);

    system_interrupt_enter_critical_section();

    DMAC->CHID.reg = DMAC_CHID_ID(resource->channel_id);
    DMAC->CHCTRLA.reg = 0;

    system_interrupt_leave_critical_section();

    /* Get transferred size */
    total_size = descriptor_section[resource->channel_id].BTCNT.reg;
    write_size = _write_back_section[resource->channel_id].BTCNT.reg;
    resource->transfered_size = total_size - write_size;

    resource->job_status = STATUS_ABORTED;
}

One difference that could explain the problem is the disabling of interrupts via system_interrupt_leave_critical_section() during the register write for aborting the dma channel.

R Bryan
  • 56
  • 4