-
Notifications
You must be signed in to change notification settings - Fork 7.3k
I2S driver on IMXRT1170_EVK causes DMA error after latest changes related to eDMA #82470
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
Comments
@Raymond0225, can you please look into this as it was bisected back to you change. Thanks |
sure, I am looking into this issue. Will get back later. |
thanks for your information. The test file is based on test_i2s_speed.c; I added another test case in this file: in this test case, I started a RX thread for reading while in the test main doing TX. To run the test added, #define I2S_SPEED_TEST_RUN_SPECIAL 1 |
speed_test.txt |
This patch does not work for me. The procedure should not be stopped because after first buffer is sent over dma the dma callback is invoked and buffer is returned the the pool. In the meantime when second buffer is sent a thread is unlocked on pool, takes the buffer and write it again. For me the buffer is 128b for 44,1k sampling therefore I have more than enough 3ms to deliver the second buffer. You can see in my debug logs that after first dma callback a write is done so the next buffer is there but after second dma callback the edma error occurs. |
I paste reference code here, it works fine on my side. There is a standalone RX thread. If you can provide you code, we can help to have a review and try. #define MY_STACK_SIZE 1024 K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE); uint32_t g_for_comparing[SAMPLE_NO]; static void fill_special(int16_t *tx_block, int att)
} static int verify_special(int16_t *buf, int att) } void my_entry_point(void dev, void * p1, void p2)
} ZTEST(drivers_i2s_speed_plus, test_i2s_transfer_special)
} |
I have CONFIG_LOG_MODE_DEFERRED=y, DMA channels are used by I2S only. Regarding my code there is nothing special about it as I just wrote you and API does not really make it possible to use differently than it is supposed to be. I will try to run you test and let you know but to be honest if you use API the way it should be used and if it's working on older kernel than there must be some loophole which is visible in some circumstances when non isolated test is performed. |
Hi @RadekPolyend , Thanks |
@DerekSnell (gdb) p/x dma_tcdpool0[31][0] you can see the third tcd has the source address as it is in the first one (I have two buffers only in the pool) but DLAST_SGA is not linked properly to the first tcd and if I am not mistaken that's how it should be. to create circularity. |
So actually when I started the code under debugging it went through the beginning and went further but the sound is still glitchy. Of course there are different things going on the board but it should not influence the behaviour of dma. It looks like there is some race condition going on which is exposed but the fact that the SW does not run in isolated test. When the SW is succesfully started the tcdpool looks like it should be but the sound is horrible (gdb) p/x dma_tcdpool0[31][0] buffers delivered to write are as they should, exchangeably Breakpoint 3, i2s_write (size=2048, mem_block=0x2000ad68 <malloc_arena+4712>, dev=0x3002ba0c <__device_dts_ord_458>) Breakpoint 2, i2s_dma_tx_callback (dma_dev=0x3002b8b8 <__device_dts_ord_455>, arg=0x3002ba0c <__device_dts_ord_458>, channel=31, status=0) Breakpoint 3, i2s_write (size=2048, mem_block=0x2000a568 <malloc_arena+2664>, dev=0x3002ba0c <__device_dts_ord_458>) Breakpoint 2, i2s_dma_tx_callback (dma_dev=0x3002b8b8 <__device_dts_ord_455>, arg=0x3002ba0c <__device_dts_ord_458>, channel=31, status=0) Breakpoint 3, i2s_write (size=2048, mem_block=0x2000ad68 <malloc_arena+4712>, dev=0x3002ba0c <__device_dts_ord_458>) the sound is horrible though |
You can try to alter the test a bit by giving it some load e.g. some FPU calculations before every write which also increase the IRQ context than you might actually experience on-site what I do. |
I also don't understand what's the point in actually stopping the dma in the dma handler while using cyclic mode. As name suggests it does not stop but go constantly and only buffers are read or updated. In other case this is no different than actually reloading TCD which causes issues for interfaces like SAI due to very strict time constraints. For audio there should be two buffers linked into loop SG and application should only be informed which buffer is "free" at the moment so that it can be filled. No DMA stopping, no dma reloading, nothing. |
DMA cyclic mode does not introduce more time constraints. if there is no block for transfer, it will stop automatically. Time constraints come from application, application need make sure there is always blocks in the TX queue otherwise a glitch happens. I noticed that there is a FIFO configuration bug in the i2s driver for TX, could you have a try on it (one line is added):
|
fifoWatermark is set in SAI_TxSetConfig so no need for that. As for the cyclic you must take into consideration that for 44k1 you need to have new buffer available in around 20us for next sample to be present. If you reload dma in IRQ you may be late if anything in the system blocked IRQs. Therefore dma transfer for SAI must be cyclic in the way it is never reloaded and application is only informed about which buffer can be filled at the moment. Application needs to deliver data on time, if it doesn't the sound will break but the transfers are still ongoing, they just never stop, dma either switched the buffers on its own or you have double buffer and set half dma interrupt. |
I know fifoWatermark is set in side SAI_TxSetConfig , but it is set based on the configuration caller input. can you have a look to the register "base->TCR1" double confirm it is not 0? in myside, it is 0. |
Actually when cyclic DMA is used fifo and the watermark logic does not have any influence. To be honest I was able to make a cyclic dma with much more simpler code than yours. All you need to do is in scatter/gather config code to link last provided block config TCD to the first one e.g. like this --- a/drivers/dma/dma_mcux_edma.c
this will link the last TCD with first one and remove DREQ flag so they will be cyclic from now on. The rest is to manage which buffer is currently processed so that others can be filled but this must be done in the peripherial driver based on dma callbacks. I've just tested this solution and it works like a charm without any artifacts in the sound. |
"Actually when cyclic DMA is used fifo and the watermark logic does not have any influence" |
That's exactly what I mean and that's why the current implementation is not actually cyclic. Moreover it's race condition prone as it can be seen on my case when sth more is going on in the system than only single loadless application. Reloading cyclic dma is completely against its name not mentioning that it is unneseccarily complicated and makes it hard to actually find and eliminate the race condition. FIFO might give you time to avoid race condition but what if sample frequency is set let's say to 192kHz or even higher which is way faster faster than 44k1 ? Are you sure even with FIFO it can be handled ? You are relying on delays but they can as well work against your favour. |
1.It is cyclic, just not what you understand. If you have a better idea how to implement the cycle with "dynamic buffer" supported, please let me know. here "dynamic buffer" means in i2s_write interface, no constrains on the value of parameter "mem_block" . |
this solution has constrained that user buffer(as source addr or destination addr) can't be changed once configured. do you have idea how to work with i2s_write and i2s_read interface with this solution? |
Yes and that's exactly what cyclic should be about. You don't change anything when it's setup. Handling it is fairly easy you commit let's say two buffers into the queue and start dma. When first dma irq occurs you take the first buffer from the queue (which was sent) and you return it to the pool. In the meantime application is unlocked so it can fill in the first buffer and provide it to the queue again. Than second irq is served so you take the second buffer from the queue and return it to the pool and the cycle continues on and on. app - fill in 1 and 2 buffer and put it to queue using i2s_write |
How to sync between DMA transfer and user input?
|
Ad.1 You can pause cyclic DMA as any other DMA by simply disable the dma request Ok I think I've pretty much made my point which is that current cyclic implementation is not in fact cyclic because it does not work without reload. Instead of reloading the buffers in the peripheral driver the dma driver does it for it and that's the fact. I understand that you can't reproduce the problem but I can and somebody in the future will and you most probably will circle back to that. If you are not willing to do sth about it that's fine with me but you know the problem is somewhere there... |
"You can pause cyclic DMA as any other DMA by simply disable the dma request" |
This issue has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 14 days. Note, that you can always re-open a closed issue at any time. |
After latest changes in eDMA and related to this I2S MCUX SAI driver eDMA error handler is invoked after first reload of buffer.
Use case:
This problem did not happen before the changes.
Breakpoint 3, i2s_mcux_write (dev=0x3002ba0c <__device_dts_ord_458>, mem_block=0x2000a568 <malloc_arena+2664>, size=2048)
at /home/radekp/projects/zephyrproject/zephyr/drivers/i2s/i2s_mcux_sai.c:1019
1019 {
(gdb) c
Continuing.
Breakpoint 3, i2s_mcux_write (dev=0x3002ba0c <__device_dts_ord_458>, mem_block=0x2000ad68 <malloc_arena+4712>, size=2048)
at /home/radekp/projects/zephyrproject/zephyr/drivers/i2s/i2s_mcux_sai.c:1019
1019 {
(gdb) c
Continuing.
Breakpoint 1, i2s_dma_tx_callback (dma_dev=0x3002b8b8 <__device_dts_ord_455>, arg=0x3002ba0c <__device_dts_ord_458>, channel=31, status=0)
at /home/radekp/projects/zephyrproject/zephyr/drivers/i2s/i2s_mcux_sai.c:261
261 {
(gdb) c
Continuing.
Breakpoint 3, i2s_mcux_write (dev=0x3002ba0c <__device_dts_ord_458>, mem_block=0x2000a568 <malloc_arena+2664>, size=2048)
at /home/radekp/projects/zephyrproject/zephyr/drivers/i2s/i2s_mcux_sai.c:1019
1019 {
(gdb) c
Continuing.
Breakpoint 1, i2s_dma_tx_callback (dma_dev=0x3002b8b8 <__device_dts_ord_455>, arg=0x3002ba0c <__device_dts_ord_458>, channel=31, status=0)
at /home/radekp/projects/zephyrproject/zephyr/drivers/i2s/i2s_mcux_sai.c:261
261 {
(gdb) c
Continuing.
Breakpoint 2, dma_mcux_edma_error_irq_handler (dev=0x3002b8b8 <__device_dts_ord_455>) at /home/radekp/projects/zephyrproject/zephyr/drivers/dma/dma_mcux_edma.c:258
(gdb) (gdb) p/x ((struct dma_mcux_edma_data*)dev->data)->data_cb[31]
$6 = {transferConfig = {srcAddr = 0x2000a568, destAddr = 0x40404020, srcTransferSize = 0x2, destTransferSize = 0x2, srcOffset = 0x4, destOffset = 0x0, minorLoopBytes = 0x4,
majorLoopCounts = 0x200}, edma_handle = {callback = 0x30027439, userData = 0x20002fe4, base = 0x40070000, tcdPool = 0x0, channel = 0x1f, header = 0x0, tail = 0x0, tcdUsed = 0x0,
tcdSize = 0x0, flags = 0x0}, dev = 0x3002b8b8, user_data = 0x3002ba0c, dma_callback = 0x30012d31, transfer_settings = {source_data_size = 0x4, dest_data_size = 0x4,
source_burst_length = 0x4, dest_burst_length = 0x4, direction = 0x1, transfer_type = 0x2, valid = 0x1, cyclic = 0x1, write_idx = 0x3, empty_tcds = 0x3}, busy = 0x1}
The situation does not happen when I revert 7145295. but the sound is glitchy.
The text was updated successfully, but these errors were encountered: