Merge branch 'fix_debug' into 'master' Fix ARM CMSIS-DAP issues See merge request projectmu/picoprobe!1
diff --git a/CMakeLists.txt b/CMakeLists.txt index c7df996..ffa907f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -17,6 +17,7 @@ src/cdc_uart.c src/get_serial.c src/sw_dp_pio.c + src/tusb_edpt_handler.c ) target_sources(picoprobe PRIVATE
diff --git a/src/main.c b/src/main.c index ac09011..1b8838b 100644 --- a/src/main.c +++ b/src/main.c
@@ -39,6 +39,7 @@ #include "cdc_uart.h" #include "get_serial.h" #include "led.h" +#include "tusb_edpt_handler.h" #include "DAP.h" // UART0 for Picoprobe debug @@ -53,7 +54,7 @@ #define TUD_TASK_PRIO (tskIDLE_PRIORITY + 2) #define DAP_TASK_PRIO (tskIDLE_PRIORITY + 1) -static TaskHandle_t dap_taskhandle, tud_taskhandle; +TaskHandle_t dap_taskhandle, tud_taskhandle; void usb_thread(void *ptr) { @@ -75,22 +76,6 @@ #define tud_vendor_flush(x) ((void)0) #endif -void dap_thread(void *ptr) -{ - uint32_t resp_len; - do { - if (tud_vendor_available()) { - tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer)); - resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer); - tud_vendor_write(TxDataBuffer, resp_len); - tud_vendor_flush(); - } else { - // Trivial delay to save power - vTaskDelay(1); - } - } while (1); -} - int main(void) { board_init();
diff --git a/src/tusb_edpt_handler.c b/src/tusb_edpt_handler.c new file mode 100644 index 0000000..a08318d --- /dev/null +++ b/src/tusb_edpt_handler.c
@@ -0,0 +1,218 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "tusb_edpt_handler.h" +#include "DAP.h" + +static uint8_t itf_num; +static uint8_t _rhport; + +volatile uint32_t _resp_len; + +uint8_t _out_ep_addr; +uint8_t _in_ep_addr; + +buffer_t USBRequestBuffer; +buffer_t USBResponseBuffer; + +void dap_edpt_init(void) { + +} + +void dap_edpt_reset(uint8_t __unused rhport) +{ + itf_num = 0; +} + +uint16_t dap_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +{ + + TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass && + PICOPROBE_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass && + PICOPROBE_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0); + + // Initialise circular buffer indices + USBResponseBuffer.packet_wr_idx = 0; + USBResponseBuffer.packet_rd_idx = 0; + USBRequestBuffer.packet_wr_idx = 0; + USBRequestBuffer.packet_rd_idx = 0; + + // Initialse full/empty flags + USBResponseBuffer.wasFull = false; + USBResponseBuffer.wasEmpty = true; + USBRequestBuffer.wasFull = false; + USBRequestBuffer.wasEmpty = true; + + uint16_t const drv_len = sizeof(tusb_desc_interface_t) + (itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t)); + TU_VERIFY(max_len >= drv_len, 0); + itf_num = itf_desc->bInterfaceNumber; + + // Initialising the OUT endpoint + + tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1); + uint8_t ep_addr = edpt_desc->bEndpointAddress; + + _out_ep_addr = ep_addr; + + // The OUT endpoint requires a call to usbd_edpt_xfer to initialise the endpoint, giving tinyUSB a buffer to consume when a transfer occurs at the endpoint + usbd_edpt_open(rhport, edpt_desc); + usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE); + + // Initiliasing the IN endpoint + + edpt_desc++; + ep_addr = edpt_desc->bEndpointAddress; + + _in_ep_addr = ep_addr; + + // The IN endpoint doesn't need a transfer to initialise it, as this will be done by the main loop of dap_thread + usbd_edpt_open(rhport, edpt_desc); + + return drv_len; + +} + +bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request) +{ + return false; +} + +// Manage USBResponseBuffer (request) write and USBRequestBuffer (response) read indices +bool dap_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + const uint8_t ep_dir = tu_edpt_dir(ep_addr); + + if(ep_dir == TUSB_DIR_IN) + { + if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE) + { + USBResponseBuffer.packet_rd_idx = (USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT; + + // This checks that the buffer was not empty in DAP thread, which means the next buffer was not queued up for the in endpoint callback + // So, queue up the buffer at the new read index, since we expect read to catch up to write at this point. + // It is possible for the read index to be multiple spaces behind the write index (if the USB callbacks are lagging behind dap thread), + // so we account for this by only setting wasEmpty to true if the next callback will empty the buffer + if(!USBResponseBuffer.wasEmpty) + { + usbd_edpt_xfer(rhport, ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len); + USBResponseBuffer.wasEmpty = ((USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT == USBResponseBuffer.packet_wr_idx); + } + + // Wake up DAP thread after processing the callback + vTaskResume(dap_taskhandle); + return true; + } + + return false; + + } else if(ep_dir == TUSB_DIR_OUT) { + + if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE) + { + // Only queue the next buffer in the out callback if the buffer is not full + // If full, we set the wasFull flag, which will be checked by dap thread + if(!buffer_full(&USBRequestBuffer)) + { + USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT; + usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE); + USBRequestBuffer.wasFull = false; + } + else { + USBRequestBuffer.wasFull = true; + } + + // Wake up DAP thread after processing the callback + vTaskResume(dap_taskhandle); + return true; + } + + return false; + } + else return false; +} + +void dap_thread(void *ptr) +{ + uint8_t DAPRequestBuffer[DAP_PACKET_SIZE]; + uint8_t DAPResponseBuffer[DAP_PACKET_SIZE]; + + do + { + while(USBRequestBuffer.packet_rd_idx != USBRequestBuffer.packet_wr_idx) + { + // Read a single packet from the USB buffer into the DAP Request buffer + memcpy(DAPRequestBuffer, &(USBRequestBuffer.data[USBRequestBuffer.packet_rd_idx]), DAP_PACKET_SIZE); + USBRequestBuffer.packet_rd_idx = (USBRequestBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT; + + // If the buffer was full in the out callback, we need to queue up another buffer for the endpoint to consume, now that we know there is space in the buffer. + if(USBRequestBuffer.wasFull) + { + vTaskSuspendAll(); // Suspend the scheduler to safely update the write index + USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT; + usbd_edpt_xfer(_rhport, _out_ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE); + USBRequestBuffer.wasFull = false; + xTaskResumeAll(); + } + + _resp_len = DAP_ProcessCommand(DAPRequestBuffer, DAPResponseBuffer); + + + // Suspend the scheduler to avoid stale values/race conditions between threads + vTaskSuspendAll(); + + if(buffer_empty(&USBResponseBuffer)) + { + memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len); + USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT; + + usbd_edpt_xfer(_rhport, _in_ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len); + } else { + + memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len); + USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT; + + // The In callback needs to check this flag to know when to queue up the next buffer. + USBResponseBuffer.wasEmpty = false; + } + xTaskResumeAll(); + } + + // Suspend DAP thread until it is awoken by a USB thread callback + vTaskSuspend(dap_taskhandle); + + } while (1); + +} + +usbd_class_driver_t const _dap_edpt_driver = +{ + .init = dap_edpt_init, + .reset = dap_edpt_reset, + .open = dap_edpt_open, + .control_xfer_cb = dap_edpt_control_xfer_cb, + .xfer_cb = dap_edpt_xfer_cb, + .sof = NULL, + #if CFG_TUSB_DEBUG >= 2 + .name = "PICOPROBE ENDPOINT" + #endif +}; + +// Add the custom driver to the tinyUSB stack +usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) +{ + *driver_count = 1; + return &_dap_edpt_driver; +} + +bool buffer_full(buffer_t *buffer) +{ + return ((buffer->packet_wr_idx + 1) % DAP_PACKET_COUNT == buffer->packet_rd_idx); +} + +bool buffer_empty(buffer_t *buffer) +{ + return (buffer->packet_wr_idx == buffer->packet_rd_idx); +}
diff --git a/src/tusb_edpt_handler.h b/src/tusb_edpt_handler.h new file mode 100644 index 0000000..46badf1 --- /dev/null +++ b/src/tusb_edpt_handler.h
@@ -0,0 +1,41 @@ +/** + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TUSB_EDPT_HANDLER_H +#define TUSB_EDPT_HANDLER_H + +#include "tusb.h" + +#include "device/usbd_pvt.h" +#include "DAP_config.h" + +#define PICOPROBE_INTERFACE_SUBCLASS 0x00 +#define PICOPROBE_INTERFACE_PROTOCOL 0x00 + +typedef struct { + uint8_t data[DAP_PACKET_COUNT][DAP_PACKET_SIZE]; + volatile uint32_t packet_wr_idx; + volatile uint32_t packet_rd_idx; + volatile bool wasEmpty; + volatile bool wasFull; +} buffer_t; + +extern TaskHandle_t dap_taskhandle, tud_taskhandle; + +/* Main DAP loop */ +void dap_thread(void *ptr); + +/* Endpoint Handling */ +void picoprobe_edpt_init(void); +uint16_t picoprobe_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool picoprobe_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request); +bool picoprobe_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +/* Helper Functions */ +bool buffer_full(buffer_t *buffer); +bool buffer_empty(buffer_t *buffer); + +#endif \ No newline at end of file
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index cb778b3..5b0b54f 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c
@@ -242,4 +242,4 @@ uint8_t const * tud_descriptor_bos_cb(void) { return desc_bos; -} +} \ No newline at end of file