-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Description
- Type: Bug
- Priority: Major
Bug
Target
Observed on K64F
Should be reproducible on any other target supporting async serial and DMA
Toolchain:
GCC_ARM
Toolchain version:
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 5.4.1 20160919 (release) [ARM/embedded-5-branch revision 240496]
mbed-cli version:
0.9.10
meed-os sha:
e7361eb with additional patch applied from NXP, "K64F: Add support for SERIAL ASYNCH API"
Use PR #3438
or use my branch at https://github.com/Patater/mbed-os/tree/k64f-serial-dma-fun)
I'd expect the bug would be reproducible any target that supports the serial async API with DMA, however.
Expected behavior
It should be possible to write out a serial port asynchronously without DMA.
Actual behavior
The write goes out the serial port, but we never receive a TX completion event (SERIAL_EVENT_TX_COMPLETE
or anything else matching SERIAL_EVENT_TX_ALL
)
If no DMA channels are available or if the DMA usage hint DMA_USAGE_NEVER
is provided, the serial driver is supposed to fall back to using interrupts. However, it looks like that fallback behavior isn't working. After a quick inspection of the code, I couldn't see where the UART's interrupt is enabled: EnableIRQ is called, but K64F's UART_EnableInterrupts
or the HAL's serial_irq_set
appears not to be called, so the UART appears to never trigger the IRQ and we never get a TX completion event at the application level.
Steps to reproduce
I was able to reproduce the issue on K64F with the following steps. However, one should be able to reproduce the issue on any other target with DEVICE_SERIAL_ASYNCH
- Make a new project (
mbed new serial_dma_fun
) - Get mbed-os from PR K64F: Add support for SERIAL ASYNCH API #3438 (or use my branch at https://github.com/Patater/mbed-os/tree/k64f-serial-dma-fun)
- Create main.cpp as below
- Compile and run (
mbed compile -m K64F -t GCC_ARM
)
/* main.cpp */
#include <stdint.h>
#include <mbed.h>
#include <rtos.h>
#if !DEVICE_SERIAL_ASYNCH
#error "This test suite requires the async serial API to be supported on this target"
#endif
/* This semaphore keeps track of whether or not the serial callback has
* finished. This semaphore allows the main thread to wait for the serial
* callback to finish. */
Semaphore serial_semaphore(0);
Timeout watchdog;
static void serial_write_callback(int events)
{
serial_semaphore.release();
}
static void fail() {
puts("\r\n\tSorry, we failed something.\r\n");
for(;;);
}
static void dma_serial_write_async(const DMAUsage usage)
{
int result;
Serial serial(USBTX, USBRX);
event_callback_t event;
const uint8_t buffer[] = "Howdy\r\n";
event.attach(serial_write_callback);
/* Configure DMA */
result = serial.set_dma_usage_tx(usage);
if (result) {
fail();
}
result = serial.set_dma_usage_rx(usage);
if (result) {
fail();
}
serial.write(buffer, sizeof(buffer) - 1, event, SERIAL_EVENT_TX_ALL);
/* Wait for the write to finish by waiting for the serial callback to
* finish. */
/* NOTE: Completion event (callback) never happens, even though we expect
* such an event, but the serial data does still go out the serial port. */
serial_semaphore.wait();
}
static void dma_serial_write_async_without_dma(void)
{
printf("-------- %s --------", __func__);
fflush(stdout);
dma_serial_write_async(DMA_USAGE_NEVER);
puts("\r\n");
fflush(stdout);
}
static void timed_out() {
/* We've timed out. */
puts("\r\n\tSorry, looks like we hung.\r\n");
}
static void setup_watchdog(void)
{
/* Set up a watchdog timer to kill things that hang. This will call our
* callback in ISR mode after 2 seconds. */
watchdog.attach(timed_out, 2.0);
}
int main(void)
{
puts("\r\nHello and welcome to serial DMA fun!\r\n");
setup_watchdog();
/* This will hang. */
dma_serial_write_async_without_dma();
puts("Congratulations! You've made it through the gauntlet!\r\n");
}
The output of running the program is as follows. The "Howdy" text is what we attempted to write out the serial port with asynchronously with the DMA hint DMA_USAGE_NEVER.
Hello and welcome to serial DMA fun!
-------- dma_serial_write_async_without_dma -------Howdy
Sorry, looks like we hung.