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

It should be possible to write out a serial port asynchronously without DMA (using DMA_USAGE_NEVER) #3535

Closed
Patater opened this issue Jan 5, 2017 · 5 comments

Comments

@Patater
Copy link
Contributor

Patater commented Jan 5, 2017

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

  1. Make a new project (mbed new serial_dma_fun)
  2. 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)
  3. Create main.cpp as below
  4. 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.

@bridadan
Copy link
Contributor

bridadan commented Jan 5, 2017

cc @0xc0170 @mmahadevan108

@mmahadevan108
Copy link
Contributor

Thanks for the test, I have submitted a PR with the fix.

@Patater
Copy link
Contributor Author

Patater commented Jan 11, 2017

Tested working with PR #3558 on both K64F and EFM32GG_STK3700 (which actually didn't have this problem, despite the original suspicion that it might).

Thanks for the fix!

@theotherjimmy
Copy link
Contributor

So, can we close this? @Patater

@Patater
Copy link
Contributor Author

Patater commented Jan 13, 2017

PR #3558 was merged.

@Patater Patater closed this as completed Jan 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants