diff --git a/ports/esp32/usb_serial_jtag.c b/ports/esp32/usb_serial_jtag.c index 86c89385fae..44b3412cd52 100644 --- a/ports/esp32/usb_serial_jtag.c +++ b/ports/esp32/usb_serial_jtag.c @@ -77,12 +77,14 @@ static void usb_serial_jtag_isr_handler(void *arg) { } if (flags & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) { - // As per the ESP-IDF driver, allow for the possibility the USJ just sent a full - // 64-bit endpoint to the host and now it's waiting for another ZLP to flush the result - // to the OS + // This interrupt is only enabled after we've sent a full IN EP buffer to the host. + + // As per the ESP-IDF driver, this is necessary if the USJ has just + // sent a full 64-byte EP to the host and now the host is waiting + // for another ZLP to signal that the transfer is actually done. usb_serial_jtag_ll_txfifo_flush(); - // Disable this interrupt until next time we write into the FIFO + // Disable this interrupt until the next time we write a full EP buffer usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); } } @@ -113,7 +115,7 @@ void usb_serial_jtag_tx_strn(const char *str, size_t len) { TickType_t start_tick = xTaskGetTickCount(); while (!usb_serial_jtag_ll_txfifo_writable()) { TickType_t now_tick = xTaskGetTickCount(); - if (!terminal_connected || now_tick > (start_tick + pdMS_TO_TICKS(200))) { + if (!terminal_connected || (now_tick - start_tick) > pdMS_TO_TICKS(200)) { terminal_connected = false; return; } @@ -122,9 +124,22 @@ void usb_serial_jtag_tx_strn(const char *str, size_t len) { l = usb_serial_jtag_ll_write_txfifo((const uint8_t *)str, l); str += l; len -= l; + } + + // Clear any old "serial in empty" interrupt, as we're about to either + // manually flush or enable the interrupt if the TXFIFO is full (no risk of + // a lost flush as we check if the TXFIFO is full after we do this.) + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + + if (usb_serial_jtag_ll_txfifo_writable()) { + // If we haven't written a full EP buffer, manually flush it to send to the host + usb_serial_jtag_ll_txfifo_flush(); + } else { + // If we have written a full EP buffer to send to the host then the hardware will flush + // automatically, but enable the "serial in empty" interrupt so we will send a ZLP once + // the full buffer has been sent. usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); } - usb_serial_jtag_ll_txfifo_flush(); } #endif // MICROPY_HW_ESP_USB_SERIAL_JTAG diff --git a/tests/serial_test.py b/tests/serial_test.py index eebea402fa4..d04c8d0f9fc 100755 --- a/tests/serial_test.py +++ b/tests/serial_test.py @@ -15,6 +15,13 @@ from test_utils import test_instance_epilog, convert_device_shortcut_to_real_device +TEST_READ_TIMEOUT = 1 + +# For ESP32 boards with both native/USJ USB and a UART serial port, soft +# reset may flush the UART buffer before it completes - this can be quite large +# if it's backed up a lot of data during the previous test. +SOFT_RESET_TIMEOUT = 5 + echo_test_script = """ import sys bytes_min=%u @@ -98,9 +105,25 @@ def drain_input(ser): time.sleep(0.1) +def reset_into_raw_repl(ser): + try: + ser.timeout = SOFT_RESET_TIMEOUT + ser.flushInput() + ser.write(b"\x03\x01\x04") # break, raw-repl, soft-reboot + EXPECTED1 = b"MPY: soft reboot\r\n" + EXPECTED2 = b"raw REPL; CTRL-B to exit\r\n>" + r1 = ser.read_until(EXPECTED1) + r2 = b"" + if r1.endswith(EXPECTED1): + r2 = ser.read_until(EXPECTED2) + if not r2.endswith(EXPECTED2): + raise TestError("could not reset to raw REPL", r1 + r2) + finally: + ser.timeout = TEST_READ_TIMEOUT + + def send_script(ser, script): - ser.write(b"\x03\x01\x04") # break, raw-repl, soft-reboot - drain_input(ser) + reset_into_raw_repl(ser) chunk_size = 32 for i in range(0, len(script), chunk_size): ser.write(script[i : i + chunk_size]) @@ -270,13 +293,13 @@ def write_test(ser_repl, ser_data, bufsize, nbuf, verified): def do_test(dev_repl, dev_data=None, time_per_subtest=1): if dev_data is None: print("REPL and data on", dev_repl) - ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=1) + ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=TEST_READ_TIMEOUT) ser_data = ser_repl else: print("REPL on", dev_repl) print("data on", dev_data) - ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=1) - ser_data = serial.Serial(dev_data, baudrate=115200, timeout=1) + ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=TEST_READ_TIMEOUT) + ser_data = serial.Serial(dev_data, baudrate=115200, timeout=TEST_READ_TIMEOUT) # Do echo test first, and abort if it doesn't pass. echo_test(ser_repl, ser_data)