S32K148 UART eDMA driver hangs at LPUART_SetTxDmaCmd

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

S32K148 UART eDMA driver hangs at LPUART_SetTxDmaCmd

Jump to solution
3,608 Views
paul12345
Contributor II

This is maybe similar to this bug related to eDMA UART driver usage, but I'm not sure:

https://community.nxp.com/t5/S32K/LPUART-eDMA-configuration/td-p/817413

I am trying to use S32K148 SDK 4.0.2 UART in eDMA mode, and through some printf insertion I found that LPUART_SetTxDmaCmd() called inside LPUART_DRV_StartSendDataUsingDma() just blocks indefinitely. This indicates something was not set up properly with the edma_driver, the uart_driver, or the clocks to the eDMA subsystem, I suspect. The affected function is just an inline register write to set the TDMAE bit of the UART BAUD register. Any tips on what to try?

It seems the driver should return some bad status (if possible) rather than get stuck in an infinite loop or IRQ. Similar to above link: do I somehow need to configure the DMA priority? I am using priority EDMA_CHN_PRIORITY_8 as a middle-of-the-road value.

Labels (1)
1 Solution
3,535 Views
paul12345
Contributor II

Ok, I've got it working now. I can send data in blocking/interrupt mode, then de-inititialize and reinitialize in non-blocking/dma mode, and continue sending data.

In the end there was nothing special about the S32DS-generated config structs (EDMA, UART, CLOCK, PINMUX configs were all correct as generated by the Eclipse tool). For example edma_channel_config_t just has default priority, a particular virtual channel number for eDMA, and the UARTx_Tx/Rx source as the S32DS GUI instructs you to configure.

The issue was the configuration of the interrupts, as I suggested above. Here's what I changed: I removed calls to INT_SYS_InstallHandler() in lpuart_driver.c; deleted g_lpuartIsr[]; named the IRQs with their default name from the .S file (LPUART0_RxTx_IRQHandler, etc); and added alwayslink and linkstatic to driver libraries which include IRQ definitions. This last step should hopefully ensure the linker throws an error if there is ambiguity in which IRQ should get used.

I don't see any particular benefit to using runtime IRQ configuration as was done in the UART driver, since the hardware is going to be designed either for LIN or UART and not runtime switchable.  So using a static IRQ name for whatever the actual PHY layer is would be fine in most cases and is much clearer. Maybe all the misdirection in that area is because LIN is only supported as a wrapper around UART0,1,2 with some other stuff added like CRCs. Regardless, since I'm not going to use LIN I don't particularly care if the UART and LIN drivers can be made to be compatible, and would prefer to just keep them separate.

View solution in original post

0 Kudos
9 Replies
3,536 Views
paul12345
Contributor II

Ok, I've got it working now. I can send data in blocking/interrupt mode, then de-inititialize and reinitialize in non-blocking/dma mode, and continue sending data.

In the end there was nothing special about the S32DS-generated config structs (EDMA, UART, CLOCK, PINMUX configs were all correct as generated by the Eclipse tool). For example edma_channel_config_t just has default priority, a particular virtual channel number for eDMA, and the UARTx_Tx/Rx source as the S32DS GUI instructs you to configure.

The issue was the configuration of the interrupts, as I suggested above. Here's what I changed: I removed calls to INT_SYS_InstallHandler() in lpuart_driver.c; deleted g_lpuartIsr[]; named the IRQs with their default name from the .S file (LPUART0_RxTx_IRQHandler, etc); and added alwayslink and linkstatic to driver libraries which include IRQ definitions. This last step should hopefully ensure the linker throws an error if there is ambiguity in which IRQ should get used.

I don't see any particular benefit to using runtime IRQ configuration as was done in the UART driver, since the hardware is going to be designed either for LIN or UART and not runtime switchable.  So using a static IRQ name for whatever the actual PHY layer is would be fine in most cases and is much clearer. Maybe all the misdirection in that area is because LIN is only supported as a wrapper around UART0,1,2 with some other stuff added like CRCs. Regardless, since I'm not going to use LIN I don't particularly care if the UART and LIN drivers can be made to be compatible, and would prefer to just keep them separate.

0 Kudos
3,548 Views
paul12345
Contributor II

Yeah, the demo uses INTERRUPT mode plus blocking transmission. You can easily swap to DMA mode, but also need to swap to non-blocking transmission. For me I think it was several things, which I'm still working through (but I'm getting closer!):

- I had separated uart_driver from lin_driver due to messy circular imports

- I was missing "alwayslink" flag in the uart_driver (I'm using bazel as a build tool), so it's possible that the linker would lazily just use the *weak* IRQ definition in the startup.S file, rather than the *normal strength* one in the lpuart driver

With these changes, I can now get lpuart2 to go through the whole application code of flushing data using the DMA driver. I don't know that it is working yet, but at least it isn't hitting an unhandled interrupt and trapping in an infinite loop. So long story short a lot of these things were growing pains due to my non-S32DS development environment.

0 Kudos
3,544 Views
Daniel_Wax
NXP Employee
NXP Employee

I wonder if we are chasing the same bug.  I have no LIN setup.  I am digging deeper and mine seems to be EDMA setup related. Somewhere in the DMA transfer the Exception occurs. the application exmaple seems to be looking for data first before sending. 

 the program counter is pointing to 0x478 WDOG_EWM_IRQ Handler.  So this may be a timeout.  I did connect an FTDI USB<>UART dongle to the pins but I have not setup anything to be sending yet.

3,556 Views
paul12345
Contributor II

I think I found the issue. I have removed the LIN drivers because they have circular imports intertwining them with the UART drivers and I don't plan on using LIN. However, I think this means that the LPUART IRQs are not properly named, resulting in the startup.S file trapping in a default handler with an infinite loop. I see these configured in the startup .S file in the relevant range:

.long LPUART0_RxTx_IRQHandler /* LPUART0 Transmit / Receive Interrupt*/
.long Reserved48_IRQHandler /* Reserved Interrupt 48*/
.long LPUART1_RxTx_IRQHandler /* LPUART1 Transmit / Receive Interrupt*/
.long Reserved50_IRQHandler /* Reserved Interrupt 50*/
.long LPUART2_RxTx_IRQHandler /* LPUART2 Transmit / Receive Interrupt*/
.long Reserved52_IRQHandler /* Reserved Interrupt 52*/
.long Reserved53_IRQHandler /* Reserved Interrupt 53*/
.long Reserved54_IRQHandler /* Reserved Interrupt 54*/

But then in the LIN driver (lin_irq.c) it is referring to them instead with these names (which do not appear anywhere in the .S file):

void LIN_LPUART0_RxTx_IRQHandler(void)
void LIN_LPUART0_ERR_IRQHandler(void)
void LIN_LPUART1_RxTx_IRQHandler(void)
void LIN_LPUART1_ERR_IRQHandler(void)
void LIN_LPUART2_RxTx_IRQHandler(void)
void LIN_LPUART2_ERR_IRQHandler(void)

But then in lpuart_irq.h they are named like this:

void LPUART0_IrqHandler(void);
void LPUART1_IrqHandler(void);
void LPUART2_IrqHandler(void);

I guess there must be some mappings that reconcile all these names at runtime with interrupt_manager.

0 Kudos
3,551 Views
Daniel_Wax
NXP Employee
NXP Employee

I can take our LPUART demo and switch the transfer type to DMA from Interrupt based

I also add a DMA channel (DMA 0 for TX, DMA 1 for RX) then I regenerate the code.  I also get an exception when executing LPUART_DRV_ReceiveData(INST_LPUART_LPUART_1, buffer, 1U);   Ill dig into it more tomorrow.  I think we are forgetting something obvious.

0 Kudos
3,580 Views
paul12345
Contributor II

I'm peppering the project with print statements to understand what is going on, and as far as I can tell there are two different issues:

1) I had to comment out the body of LPUART_SetTxDmaCmd() because it just hangs indefinitely:

base->BAUD = (base->BAUD & ~LPUART_BAUD_TDMAE_MASK) | ((enable ? 1UL : 0UL) << LPUART_BAUD_TDMAE_SHIFT);

How could this be? It's setting a bit in a register, so either the SDK has the register address incorrect or the clock or power supply is missing. Or, there's a bug in the silicon. Or, maybe writing this bit has some side effect like immediately causing an interrupt and that is causing other issues.

2) LPUART_DRV_TxDmaCallback is never getting called, despite the uart_driver registering it as an EDMA callback target using EDMA_DRV_InstallCallback(). This is in LPUART_DRV_StartSendDataUsingDma() and returns STATUS_SUCCESS. Obviously the consequence is that if that UART/DMA callback is never called then my callback function which is registered with the uart_driver is also never called.

 

Issue (2) is probably a consequence of issue (1), since the TDMAE bit says "TDMAE configures the transmit data register empty flag, STAT[TDRE], to generate a DMA request." So obviously if I comment that out then DMA requests would not occur.

0 Kudos
3,582 Views
paul12345
Contributor II

It is not an eclipse project, so not easy to share, but the bulk of the configurations I am experimenting with are shown here:

https://community.nxp.com/t5/S32K/S32K146-UART-DMA-usage-problems/m-p/1366448/highlight/true#M12647

If I change trigger to true or source to none (rather than LPUARTx_Tx/Rx as a source) then the configuration at least completes. But, this conflicts with the generated code from S32DS which suggests that trigger = false and source should be the appropriate LPUARTx_Tx/Rx enum value. In either case, I get no data out and the callbacks appear never to be called. It seems like maybe the EDMA driver is not configuring the DMAMUX appropriately, or else it is hanging on some configuration section because the clock is off or the peripheral is powered down. I can't explain why a simple register write would trap except if the peripheral was not getting a clock. I did notice that all S32DS examples show this, which to me looks a bit odd because I would assume the DMAMUX0_CLK needs a clock source. However, it is not a selectable block that I see in the S32DS clock configurator.

{
.clockName = DMAMUX0_CLK,
.clkGate = true,
.clksrc=CLK_SRC_OFF,
.frac = MULTIPLY_BY_ONE,
.divider = DIVIDE_BY_ONE,
},

0 Kudos
3,605 Views
paul12345
Contributor II

I found that it can proceed if I set enableTrigger = true. My understanding was that it is a periodic trigger to kick the DMA to transfer data...but why would this be required to be set to true? Can someone please explain how the trigger relates to DMA UART usage? According to Section 17.2.3, using Periodic Trigger mode is different than Normal mode and results in triggering the DMA transfer using the periodic interrupt timer (which is SysTick @ 1ms, by default, I think). I'd like to understand why the TRGMUX needs to be used in periodic trigger mode for the UART DMA driver to work.

0 Kudos
3,588 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hi Paul,

Can you please share the project so that we can reproduce it?

 

BR, Daniel

0 Kudos