Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] DMA XFERCFG::XFERCOUNT overflow when submitting large I2S DMA transfer #186

Open
maciejmatuszak opened this issue Apr 4, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@maciejmatuszak
Copy link

Describe the bug
Submitting large I2S DMA Rx transfer using I2S_RxTransferReceiveDMA(...) splits the transfer into multiple DMA transfers. This happens if I2S_GetTransferBytes() reduce the size. I2S_GetTransferBytes calculates max single DMA transfer which is limited by hardware registers XFERCFGn:XFERCOUNT and XFERCFGn:WIDTH however the function I2S_GetTransferBytes uses:

#define DMA_MAX_TRANSFER_BYTES (DMA_MAX_TRANSFER_COUNT * sizeof(uint32_t))

Which assumes frame width of 4 bytes (sizeof(uint32_t)).
This leads to I2S_GetTransferBytes() limiting it to 4096 bytes. The HW register XFERCFGn is set by DMA_SetChannelXferConfig()
which tries to set the XFERCFGn:XFERCOUNT field to 4096/2=2048 which is bigger than the 10 bits the XFERCFGn:XFERCOUNT allows. As a result only half of the 4096 bytes buffer to be filled.

To Reproduce

  • Environment (please complete the following information):
    • Tag/Commit hash: MCUX_2.14.0
    • Toolchain: arm-none-eabi-g++ (15:10.3-2021.07-4) 10.3.1 20210621 (release)
    • Board/SoC: LPCXpresso55S69
    • Example: lpcxpresso55s69_i2s_dma_record_playback
  • Steps to reproduce the behavior:
  1. set the s_Buffer to 4096 bytes:
__ALIGN_BEGIN static uint8_t s_Buffer[4096] __ALIGN_END;
  1. Update the s_RxConfig.oneChannel = true; This will set the frame length yo 2 bytes
    I2S_RxGetDefaultConfig(&s_RxConfig);
    s_RxConfig.divider     = DEMO_I2S_CLOCK_DIVIDER;
    s_RxConfig.oneChannel = true;
    s_RxConfig.masterSlave = DEMO_I2S_RX_MODE;
  1. after the s_RxTransfer is updated set the s_Buffer to known values using memset
    s_RxTransfer.data     = &s_Buffer[0];
    s_RxTransfer.dataSize = sizeof(s_Buffer);
    memset(s_Buffer, 'r', sizeof(s_Buffer));
  1. Put breakpoint in RxCallback
  2. Execute the code till the RxCallback is called
  3. Examine the s_Buffer only half of it has data second half has the original r characters

Expected behavior
The s_Buffer should be filled completly

Screenshots and console output
image

Additional context

@maciejmatuszak maciejmatuszak added the bug Something isn't working label Apr 4, 2024
@maciejmatuszak
Copy link
Author

This is How I fixed it:

static uint16_t I2S_GetTransferBytes(i2s_dma_handle_t *handle, volatile i2s_transfer_t *transfer)
{
    assert(transfer != NULL);

    uint16_t transferBytes;

    if (transfer->dataSize >( DMA_MAX_TRANSFER_COUNT*handle->bytesPerFrame))
    {
        transferBytes = DMA_MAX_TRANSFER_COUNT*handle->bytesPerFrame;
        if ((transferBytes % 4U) != 0U)
        {
            transferBytes -= (transferBytes % 4U);
        }
    }
    else
    {
        transferBytes = (uint16_t)transfer->dataSize;
    }

    return transferBytes;
}

Also I put assert in DMA_SetChannelXferConfig

static inline uint32_t DMA_SetChannelXferConfig(
    bool reload, bool clrTrig, bool intA, bool intB, uint8_t width, uint8_t srcInc, uint8_t dstInc, uint32_t bytes)
{
    assert(((uint32_t)bytes / (uint32_t)width - 1UL) < DMA_MAX_TRANSFER_COUNT);
    return (DMA_CHANNEL_XFERCFG_CFGVALID_MASK | DMA_CHANNEL_XFERCFG_RELOAD(reload) |
            DMA_CHANNEL_XFERCFG_CLRTRIG(clrTrig) | DMA_CHANNEL_XFERCFG_SETINTA(intA) |
            DMA_CHANNEL_XFERCFG_SETINTB(intB) |
            DMA_CHANNEL_XFERCFG_WIDTH((uint32_t)width == 4UL ? 2UL : ((uint32_t)width - 1UL)) |
            DMA_CHANNEL_XFERCFG_SRCINC((uint32_t)srcInc == 4UL ? ((uint32_t)srcInc - 1UL) : (uint32_t)srcInc) |
            DMA_CHANNEL_XFERCFG_DSTINC((uint32_t)dstInc == 4UL ? ((uint32_t)dstInc - 1UL) : (uint32_t)dstInc) |
            DMA_CHANNEL_XFERCFG_XFERCOUNT((uint32_t)bytes / (uint32_t)width - 1UL));
}

@maciejmatuszak maciejmatuszak changed the title [BUG] DMA XFERCFG::XFERCOUNT overflow when submitting large I2S transfer [BUG] DMA XFERCFG::XFERCOUNT overflow when submitting large I2S DMA transfer Apr 4, 2024
@mcuxsusan
Copy link
Contributor

Thanks for raising up the issue and suggest fix, I have forwarded the issue to internal developer, but the feedback maybe delayed, appreciate for your patience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants