8000 esp32/usb: Wake main thread when usb receives data. by andrewleech · Pull Request #12845 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

esp32/usb: Wake main thread when usb receives data. #12845

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

Merged
merged 2 commits into from
Nov 1, 2023

Conversation

andrewleech
Copy link
Contributor

This improves latency on stdin on esp32 parts with built in (tinyusb based) USB like S2 and S3.

The latency is highly visible when importing a module on a mpremote mount session, directly slowing down the import process.

With a 14KB python file in mpremote mount it brings the import time from 12.1 sec down to 4.2 sec
When combined with #12836 the import time drops to ~900ms

STEP: ~ $ mpremote mount .
Local directory . is mounted at /remote
Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] or Ctrl-x to exit this shell
>
MicroPython v1.22.0-preview.82.g0bcd3c5734.dirty on 2023-11-01; LOLIN_S2_MINI with ESP32-S2FN4R2
Type "help()" for more information.
>>> import time;s = time.ticks_ms();import big_module; print(time.ticks_diff(time.ticks_ms(), s))
4206

@dpgeorge
Copy link
Member
dpgeorge commented Nov 1, 2023

This is a very good improvement!

@@ -58,6 +59,7 @@ static void usb_callback_rx(int itf, cdcacm_event_t *event) {
ringbuf_put(&stdin_ringbuf, usb_rx_buf[i]);
}
}
mp_hal_wake_main_task_from_isr();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this USB callback actually an ISR? Or should we instead be using vTaskNotifyGive(mp_main_task_handle) instead?

Copy link
Contributor Author
@andrewleech andrewleech Nov 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8000

Good question! I've tried to trace it through the code path in micropython/ports/esp32/managed_components/espressif__tinyusb/ - this function is called indictly by TinyUSB's "standard" tud_cdc_rx_cb callback which eventually traces back up to the top level TinyUSB task tud_task_ext() / tud_task() - so, no, it's not an IRQ it's running directly from a separate FreeRTOS Task.

edit: missed the important not above

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pushed the change, xTaskNotifyGive(mp_main_task_handle); does provide the same speed up thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which eventually traces back up to the top level TinyUSB task tud_task_ext() / tud_task() - so, no, it's an IRQ it's running directly from a separate FreeRTOS Task.

So is it an IRQ, or is it running at thread-level on a separate task? I'm guessing the latter.

Copy link
Contributor Author
@andrewleech andrewleech Nov 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thread-level on a separate task.


image

ports/esp32/managed_components/espressif__esp_tinyusb_mod/tusb_tasks.c

/**
 * @brief This top level thread processes all usb events and invokes callbacks
 */
static void tusb_device_task(void *arg)
{
    ESP_LOGD(TAG, "tinyusb task started");
    while (1) { // RTOS forever loop
        tud_task();
    }
}

esp_err_t tusb_run_task(void)
{
    // This function is not garanteed to be thread safe, if invoked multiple times without calling `tusb_stop_task`, will cause memory leak
    // doing a sanity check anyway
    ESP_RETURN_ON_FALSE(!s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task already started");
    // Create a task for tinyusb device stack:
    xTaskCreate(tusb_device_task, "TinyUSB", CONFIG_TINYUSB_TASK_STACK_SIZE, NULL, CONFIG_TINYUSB_TASK_PRIORITY, &s_tusb_tskh);
    ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_FAIL, TAG, "create TinyUSB main task failed");
    return ESP_OK;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, thanks for clarifying.

(I'm surprised it's a simple while-1 loop... hopefully tud_task() is a blocking call that waits for events on a FreeRTOS queue.)

@@ -58,6 +59,7 @@ static void usb_callback_rx(int itf, cdcacm_event_t *event) {
ringbuf_put(&stdin_ringbuf, usb_rx_buf[i]);
}
}
xTaskNotifyGive(mp_main_task_handle);
Copy link
Member
8000

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to put this in a function called mp_hal_wake_main_task().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -41,7 +41,8 @@
static uint8_t usb_rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE];

static void usb_callback_rx(int itf, cdcacm_event_t *event) {
// TODO: what happens if more chars come in during this function, are they lost?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding a comment (on line 42 just before the function), that it's called from a separate FreeRTOS task (and not an ISR).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea, done

@dpgeorge
Copy link
Member
dpgeorge commented Nov 1, 2023

For some reason your author email address is totally empty??

@andrewleech
Copy link
Contributor Author

Oh that's strange. Seems the git config for that repo got an email = line added to it at some point...

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
This improves (decreases) the latency on stdin, on SoCs with built-in USB
and using TinyUSB, like S2 and S3.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
@projectgus
Copy link
Contributor
projectgus commented Nov 1, 2023

Nice work, @andrewleech!

In the context of #12846 and trying to support Python callbacks from USB code, I've been wondering about the relative merits of running TinyUSB in its own task as ESP-IDF does. I think it probably is worthwhile on dual-core systems, although I'd also like to measure it. If we did it more similarly to the other ports then some of these problems would go away (USB interrupt could schedule the TinyUSB handler to run in the main task.) Plus would reclaim some RAM (the handler task stack).

@dpgeorge dpgeorge merged commit 06a7bf9 into micropython:master Nov 1, 2023
@andrewleech andrewleech deleted the esp32_usb_stdin branch November 1, 2023 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
0