How do i reset a DMA channel in the middle of the transfer RSL 10

when using the DMA i need to disable it, and re enable but it is waiting for the transfer to finish, this means that it wont be disable as there will not be another incoming transfer.

how do i reset a dma channel??

thx sean

From the Hardware Reference Manual;

12.2.2 DMA Channel Configuration
The DMA has eight independently configurable channels. To enable or disable a DMA channel, configure the DMA_CTRL0_ENABLE bit from the appropriate DMA_CTRL0 register. If the DMA channel is in the midst of an operation when it is disabled, the operation is aborted. All pending bus requests are subsequently aborted when the channel is disabled.

So disabling the DMA should allow you to reconfigure it and then reenable it to start a new DMA transfer.

If this is what you’re doing, why do you believe it is “waiting for the transfer to finish” and not getting disabled?

Thank you for using the Community Forum.

look at the description of disable, waits for the currant transfer to complete, the state dosn’t change when we disable

Disable DMA channel (channel will
wait on current transfer before
stopping)

this fixes the issue!!!

void kickSpiHw(void) {

Sys_SPI_Config(SPI_DRIVER_HW_INSTANCE, SPI0_OVERRUN_INT_ENABLE | SPI0_UNDERRUN_INT_ENABLE | SPI0_CONTROLLER_CM3 |

                                         SPI0_SELECT_SLAVE | SPI0_CLK_POLARITY_NORMAL | SPI0_MODE_SELECT_AUTO |

                                         SPI0_ENABLE | SPI0_PRESCALE_1024);

Sys_SPI_Config(SPI_DRIVER_HW_INSTANCE, SPI0_OVERRUN_INT_ENABLE | SPI0_UNDERRUN_INT_ENABLE | SPI0_CONTROLLER_DMA |

                                         SPI0_SELECT_SLAVE | SPI0_CLK_POLARITY_NORMAL | SPI0_MODE_SELECT_AUTO |

                                         SPI0_ENABLE | SPI0_PRESCALE_1024);

}

2 Likes

The description of the enable bit is referring to a single DMA transfer that is in process. A DMA transfer is a multi-step event and once started it will finish. Take for example a peripheral to memory transfer described in section 12.2.4.3 of the Hardware Reference Manual:

12.2.4.3 Peripheral-to-Memory (PM)
The DMA peripheral-to-memory mode is used to transfer data from a peripheral to the Arm Cortex-M3 processor’s
data memory. If the peripheral is configured to operate in DMA mode, the operation sequence is as follows (assuming
32-bit operation):

  1. The peripheral generates a new data word.
  2. The peripheral asserts a signal on a DMA request line (indicating that the buffer is full).
  3. The DMA channel receives a DMA request from the peripheral.
  4. Through the peripheral bus bridge, the DMA requests access to the peripheral bus.
  5. The DMA is granted access to the peripheral bus.
  6. The DMA reads the word at the next source address and stores the word to a temporary register.
  7. The DMA releases the peripheral bus.
  8. The DMA channel acknowledges the peripheral DMA request (implied by the previous read operation).
  9. The DMA requests access to the Arm Cortex-M3 processor’s data memory via the Arm Cortex-M3
    processor’s data memory arbiter.
  10. The DMA is granted access to the Arm Cortex-M3 processor’s data memory.
  11. The DMA writes the word at the next destination address.
  12. The DMA releases the Arm Cortex-M3 processor’s data memory.
  13. The DMA channel’s counter is incremented.
  14. If the transfer length is not reached, the DMA waits for the next peripheral DMA request. If the transfer length
    is reached and the DMA is in linear mode, the DMA channel switches to the complete state. If the DMA is in
    circular mode, the DMA resets the counter to 0 and remains enabled.
  15. Any DMA interrupts that are triggered by this transfer are generated.

If the DMA channel is disabled and the DMA engine is in the middle of this process, for instance step 5, then this transfer will complete. All further transfers will be disabled and the DMA_DISABLED interrupt will be generated if enabled. So you should be able to use the DMA_DISABLED interrupt for confirmation the DMA channel has stopped.

the state machine is in state 1 when it is cancelled and no more data is incoming and the disable is blocked. kicking the SPI resolves the issue.

when we come to reconfigure the DMA we check the state is idle to be safe as this is a medical device!

it is stuck in state 1 therefore we reproduce an error, this kick resolves the issue.

like magic!

from the spec on disable!

Thank you for the background. Glad you found a work-around!

I was unable to reproduce this behavior with the DMA setup I was using. Since this behavior seems to be different than what you expected from the documentation - could you share the DMA setup you are using?

Thank you for using the Community Forum.

we are an spi slave!

/* ----------------------------------------------------------------------------

  • Function : void spiHalFrameSetup(spiHalTransferCtrlData transferData)


  • Description : setup the transfer

  • Inputs : transferData control data for the transfer

  • Outputs : bool false = failure

  • Assumptions : None

  • ------------------------------------------------------------------------- */

bool spiHalFrameSetup(spiHalTransferCtrlData transferData) {

const static uint32_t zero = 0;

#if DMA_HAL_DEBUG

PRINTF(“Spi Frame setup Rxlength = %d\n”, transferData.rxLen);

PRINTF(“Spi Frame setup Txlength = %d\n”, transferData.txLen);

PRINTF(“Spi Frame setup RxAddr = 0x%X\n”, transferData.rxData);

PRINTF(“Spi Frame setup TxAddr = 0x%X\n”, transferData.txData);

{

uint32_t tempCounter = 0;

for (tempCounter = 0; tempCounter < transferData.txLen; tempCounter++)

{

  PRINTF("Spi Frame setup TxDATA = 0x%X RX Data = 0x%X\n", transferData.txData[tempCounter],

         transferData.rxData[tempCounter]);

}

}

#endif /// DMA_HAL_DEBUG

bool sendZeros = false;

if (spiReady == ctrl.state) {

if (transferData.txData && transferData.rxData && (1 < transferData.txLen) && transferData.rxLen) {

  ctrl.state = spiFullDuplex;

  spiWriteData(*transferData.txData);

  transferData.txData++;

} else if (transferData.txData && (1 < transferData.txLen)) {

  ctrl.state = spiHalfDuplexTransmit;

  spiWriteData(*transferData.txData);

  transferData.txData++;

} else if (transferData.rxData && transferData.rxLen) {

  ctrl.state = spiHalfDuplexReceive;

  if (1 == transferData.txLen) {

    spiWriteData(*transferData.txData);

  } else {

    spiWriteData(0);

  }

  sendZeros = true;

} else {

  return (false);

}

} else {

#if DMA_HAL_DEBUG

PRINTF("Spi Frame setup bad state = %d\n", ctrl.state);

#endif // DMA_HAL_DEBUG

return (false);

}

spiHalTransferCtrl = transferData;

{

dmaTransferReq stDmaTransferTransmit;

stDmaTransferTransmit.channelNo = DMA_CH_NUM_SPI_TX;

stDmaTransferTransmit.blockSizeDestination = dmaBlockSize32Bits;

stDmaTransferTransmit.blockSizeSource = dmaBlockSize32Bits;

stDmaTransferTransmit.dmaTerminusDestination = dmaTerminusSpi0;

if (sendZeros) {

  stDmaTransferTransmit.dmaTerminusSource = dmaTerminusMemorySingleLocation;

  transferData.txData = (uint32_t*)&zero;

  transferData.txLen = 512;  // TODO make this a define

} else {

  stDmaTransferTransmit.dmaTerminusSource = dmaTerminusMemory;

}

stDmaTransferTransmit.lengthInBytes = transferData.txLen << 2;

stDmaTransferTransmit.priority = 0;

stDmaTransferTransmit.dmaDestination = (void*)&(ctrlReg->TX_DATA);

stDmaTransferTransmit.dmaSource = transferData.txData;

stDmaTransferTransmit.interruptsRequested = DMA_START_INT_ENABLE | DMA_COUNTER_INT_ENABLE |

                                            DMA_COMPLETE_INT_ENABLE | DMA_ERROR_INT_ENABLE | DMA_DISABLE_INT_ENABLE;

dmaHalRequestTransfer(stDmaTransferTransmit);

}

{

dmaTransferReq stDmaTransferReceive;

stDmaTransferReceive.channelNo = DMA_CH_NUM_SPI_RX;

stDmaTransferReceive.blockSizeDestination = dmaBlockSize32Bits;

stDmaTransferReceive.blockSizeSource = dmaBlockSize32Bits;

stDmaTransferReceive.dmaTerminusDestination = dmaTerminusMemory;

stDmaTransferReceive.dmaTerminusSource = dmaTerminusSpi0;

stDmaTransferReceive.lengthInBytes = transferData.rxLen << 2;

stDmaTransferReceive.priority = 0;

stDmaTransferReceive.dmaDestination = transferData.rxData;

stDmaTransferReceive.dmaSource = (void*)&(ctrlReg->RX_DATA);

stDmaTransferReceive.interruptsRequested = DMA_START_INT_ENABLE | DMA_COUNTER_INT_ENABLE | DMA_COMPLETE_INT_ENABLE |

                                           DMA_ERROR_INT_ENABLE | DMA_DISABLE_INT_ENABLE;

dmaHalRequestTransfer(stDmaTransferReceive);

}

return (true);

}
we use are own driver as the one from ON semi we were informed was blocking by the FAE,

basically we stop a transfer on the receive side when we read the header of the message to update the length, for a zero length payload this becomes an issue as the dma needs another transfer to disable the channel

Thank you for the code. I was investigating if there was an undocumented feature in the device regarding DMA operations from the SPI interface, but I cannot replicate the issue.

Here’s the test snippet of code I used:

volatile uint32_t dbg_Stat1, dbg_Stat2, dbg_Stat3;
/* Check SPI DMA status */
Sys_DMA_ClearChannelStatus(RX_DMA_NUM);
dbg_Stat1 = DMA->STATUS[RX_DMA_NUM];
PRINTF("Pre-enable status: %x\n", dbg_Stat1);

uint32_t dma_rx_config = (DMA_DEST_WORD_SIZE_32 | DMA_SRC_WORD_SIZE_32 |
		DMA_TRANSFER_P_TO_M | DMA_PRIORITY_0 | DMA_START_INT_ENABLE |
  	DMA_COUNTER_INT_ENABLE | DMA_COMPLETE_INT_ENABLE |
  	DMA_ERROR_INT_ENABLE | DMA_DISABLE_INT_ENABLE | DMA_DEST_ADDR_INC |
  	DMA_SRC_ADDR_STATIC | DMA_SRC_SPI0 | DMA_DISABLE);

/* Configure and enable SPI DMA with transfer length of 0 */
Sys_DMA_ChannelConfig(
        RX_DMA_NUM,
  	dma_rx_config,
        0,
        0,
        (uint32_t)&(SPI0->RX_DATA),
        (uint32_t)&spi_buf[0]);

Sys_DMA_ClearChannelStatus(RX_DMA_NUM);
Sys_DMA_ChannelEnable(RX_DMA_NUM);

/* Check SPI DMA status */
Sys_Delay_ProgramROM(10);
dbg_Stat2 = DMA->STATUS[RX_DMA_NUM];
PRINTF("Post-enable status: %x\n", dbg_Stat2);

/* Disable SPI DMA */
Sys_DMA_ClearChannelStatus(RX_DMA_NUM);
Sys_DMA_ChannelDisable(RX_DMA_NUM);

/* Check SPI DMA status */
Sys_Delay_ProgramROM(10);
dbg_Stat3 = DMA->STATUS[RX_DMA_NUM];
PRINTF("Post-disable status: %x\n", dbg_Stat3);

I sent no data in on the SPI interface. The result of this is:
Pre-enable status: 0x0
Post-enable status: 0x22
Post-disable status: 0x1

These are the expected DMA status indications where the DMA is checking the source and destination address when enabled, and idle when disabled. The low-nibble of the status is showing that a START interrupt and DISABLE interrupt was fired at the expected time.

Looking at your code, is it possible the interrupt handler is not handling the DISABLED interrupt correctly and not updating the ctrl.state variable? The code you shared doesn’t show where the DMA status is queried so I’m not sure I’m replicating the issue you’re seeing.