/** * @file SWO.c * @author windowsair * @brief SWO support * @change: 2021-02-17: Add basic functions * @version 0.2 * * @date 2021-02-17 * * @copyright Copyright (c) 2021 * */ /* * Copyright (c) 2013-2017 ARM Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ---------------------------------------------------------------------- * * $Date: 1. December 2017 * $Revision: V2.0.0 * * Project: CMSIS-DAP Source * Title: SWO.c CMSIS-DAP SWO I/O * *---------------------------------------------------------------------------*/ #include "DAP_config.h" #include "cmsis-dap/include/DAP.h" #include "cmsis-dap/include/uart_modify.h" #include "cmsis-dap/include/swo.h" #include "esp_err.h" EventGroupHandle_t kSwoThreadEventGroup; #if (SWO_STREAM != 0) #ifdef DAP_FW_V1 #error "SWO Streaming Trace not supported in DAP V1!" #endif #endif #if (SWO_UART != 0) #ifndef USART_PORT #define USART_PORT UART_NUM_0 /* USART Port Number */ #endif // use in baudrate setting static uint8_t USART_Ready = 0U; #endif /* (SWO_UART != 0) */ #if ((SWO_UART != 0) || (SWO_MANCHESTER != 0)) #define SWO_STREAM_TIMEOUT pdMS_TO_TICKS(50) /* Stream timeout in ms */ #define USB_BLOCK_SIZE 512U /* USB Block Size */ #define TRACE_BLOCK_SIZE 64U /* Trace Block Size (2^n: 32...512) */ // Trace State static uint8_t TraceTransport = 0U; /* Trace Transport */ static uint8_t TraceMode = 0U; /* Trace Mode */ static uint8_t TraceStatus = 0U; /* Trace Status without Errors */ static uint8_t TraceError[2] = {0U, 0U}; /* Trace Error flags (banked) */ static uint8_t TraceError_n = 0U; /* Active Trace Error bank */ // Trace Buffer static uint8_t kSwoTraceBuf[SWO_BUFFER_SIZE]; /* Trace Buffer (must be 2^n) */ static volatile uint32_t TraceIndexI = 0U; /* Incoming Trace Index */ static volatile uint32_t TraceIndexO = 0U; /* Outgoing Trace Index */ static volatile uint8_t TraceUpdate; /* Trace Update Flag */ static uint32_t TraceBlockSize; /* Current Trace Block Size */ #if (TIMESTAMP_CLOCK != 0U) // Trace Timestamp static volatile struct { uint32_t index; uint32_t tick; } TraceTimestamp; #endif // Trace Helper functions static void ClearTrace(void); static void ResumeTrace(void); static uint32_t GetTraceCount(void); static uint8_t GetTraceStatus(void); #if (SWO_STREAM != 0) volatile uint8_t kSwoTransferBusy = 0U; /* Transfer Busy Flag */ static uint32_t TransferSize; /* Current Transfer Size */ #endif #if (SWO_UART != 0) // Enable or disable UART SWO Mode // enable: enable flag // return: 1 - Success, 0 - Error uint32_t UART_SWO_Mode(uint32_t enable) { int32_t status; USART_Ready = 0U; uart_config_t uart_config = { .baud_rate = 74880, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE}; my_uart_param_config(USART_PORT, &uart_config); // register setting #define BUF_SIZE (1024) //// TODO: remove this if (enable != 0U) { my_uart_param_config(USART_PORT, &uart_config); status = my_uart_driver_install(USART_PORT, BUF_SIZE, 0, 0, NULL, 0); if (status != ESP_OK) { my_uart_driver_delete(USART_PORT); return (0U); } } else { my_uart_driver_delete(USART_PORT); } return (1U); } // Configure UART SWO Baudrate // baudrate: requested baudrate // return: actual baudrate or 0 when not configured uint32_t UART_SWO_Baudrate(uint32_t baudrate) { //// TODO: There may be bugs. //int32_t status; uint32_t index; uint32_t remain_trace_block_size; if (baudrate > SWO_UART_MAX_BAUDRATE) { baudrate = SWO_UART_MAX_BAUDRATE; } if (TraceStatus & DAP_SWO_CAPTURE_ACTIVE) { TraceIndexI += my_uart_get_rx_buffered_data_len(USART_PORT); my_uart_flush(USART_PORT); // pUSART->Control(ARM_USART_CONTROL_RX, 0U); // if (pUSART->GetStatus().rx_busy) // { // TraceIndexI += pUSART->GetRxCount(); // pUSART->Control(ARM_USART_ABORT_RECEIVE, 0U); // } } ///////////// my_uart_set_baudrate(USART_PORT, baudrate); USART_Ready = 1U; if (TraceStatus & DAP_SWO_CAPTURE_ACTIVE) { if ((TraceStatus & DAP_SWO_CAPTURE_PAUSED) == 0U) { index = TraceIndexI & (SWO_BUFFER_SIZE - 1U); // TraceIndexI % SWO_BUFFER_SIZE remain_trace_block_size = TRACE_BLOCK_SIZE - (index & (TRACE_BLOCK_SIZE - 1U)); // index % TRACE_BLOCK_SIZE TraceBlockSize = remain_trace_block_size; //pUSART->Receive(&kSwoTraceBuf[index], num); my_uart_read_bytes_async_swo(index, remain_trace_block_size); } //pUSART->Control(ARM_USART_CONTROL_RX, 1U); } return (baudrate); } // Control UART SWO Capture // active: active flag // return: 1 - Success, 0 - Error uint32_t UART_SWO_Control(uint32_t active) { int32_t status; if (active) { if (!USART_Ready) { return (0U); } TraceBlockSize = 1U; status = my_uart_read_bytes_async_swo(0, 1U); if (status == ESP_FAIL) { return (0U); } // status = pUSART->Control(ARM_USART_CONTROL_RX, 1U); // if (status != ARM_DRIVER_OK) // { // return (0U); // } } else { // no active TraceIndexI += my_uart_get_rx_buffered_data_len(USART_PORT); my_uart_flush(USART_PORT); // pUSART->Control(ARM_USART_CONTROL_RX, 0U); // if (pUSART->GetStatus().rx_busy) // { // TraceIndexI += pUSART->GetRxCount(); // pUSART->Control(ARM_USART_ABORT_RECEIVE, 0U); // } } return (1U); } // Start UART SWO Capture // index: trace buffer index to read // num: number of bytes to capture static void UART_SWO_Capture(uint32_t index, uint32_t num) { TraceBlockSize = num; my_uart_read_bytes_async_swo(index, num); } // Get UART SWO Pending Trace Count // return: number of pending trace data bytes static uint32_t UART_SWO_GetCount(void) { //// TODO: There may be bugs. // if (pUSART->GetStatus().rx_busy) // { // count = pUSART->GetRxCount(); // } // else // { // count = 0U; // } return my_uart_get_rx_buffered_data_len(USART_PORT); } #endif /* (SWO_UART != 0) */ // // Trace status helper functions // // Clear Trace Errors and Data static void ClearTrace(void) { #if (SWO_STREAM != 0) if (TraceTransport == 2U) // Indicates that we use WinUSB for transfer. { if (kSwoTransferBusy != 0U) { // Unfortunately, we cannot abort the transmission // SWO_AbortTransfer(); kSwoTransferBusy = 0U; } } #endif TraceError[0] = 0U; TraceError[1] = 0U; TraceError_n = 0U; TraceIndexI = 0U; TraceIndexO = 0U; #if (TIMESTAMP_CLOCK != 0U) TraceTimestamp.index = 0U; TraceTimestamp.tick = 0U; #endif } // Resume Trace Capture static void ResumeTrace(void) { uint32_t index_i; uint32_t index_o; if (TraceStatus == (DAP_SWO_CAPTURE_ACTIVE | DAP_SWO_CAPTURE_PAUSED)) { index_i = TraceIndexI; index_o = TraceIndexO; if ((index_i - index_o) < SWO_BUFFER_SIZE) { index_i &= SWO_BUFFER_SIZE - 1U; switch (TraceMode) { case DAP_SWO_UART: TraceStatus = DAP_SWO_CAPTURE_ACTIVE; UART_SWO_Capture(index_i, 1U); break; default: break; } } } } // Get Trace Count // return: number of available data bytes in trace buffer static uint32_t GetTraceCount(void) { uint32_t count; if (TraceStatus == DAP_SWO_CAPTURE_ACTIVE) { do { TraceUpdate = 0U; count = TraceIndexI - TraceIndexO; switch (TraceMode) { case DAP_SWO_UART: count += UART_SWO_GetCount(); break; default: break; } vTaskDelay(pdMS_TO_TICKS(10)); } while (TraceUpdate != 0U); // Synchronously wait for the data to complete } else { count = TraceIndexI - TraceIndexO; } return (count); } // Get Trace Status (clear Error flags) // return: Trace Status (Active flag and Error flags) static uint8_t GetTraceStatus(void) { uint8_t status; uint32_t n; n = TraceError_n; TraceError_n ^= 1U; status = TraceStatus | TraceError[n]; TraceError[n] = 0U; return (status); } // Set Trace Error flag(s) // flag: error flag(s) to set void SetTraceError(uint8_t flag) { TraceError[TraceError_n] |= flag; } // Process SWO Transport command and prepare response // request: pointer to request data // response: pointer to response data // return: number of bytes in response (lower 16 bits) // number of bytes in request (upper 16 bits) uint32_t SWO_Transport(const uint8_t *request, uint8_t *response) { uint8_t transport; uint32_t result; if ((TraceStatus & DAP_SWO_CAPTURE_ACTIVE) == 0U) { transport = *request; switch (transport) { case 0U: case 1U: #if (SWO_STREAM != 0) case 2U: #endif TraceTransport = transport; result = 1U; break; default: result = 0U; break; } } else { result = 0U; } *response = result ? DAP_OK : DAP_ERROR; return ((1U << 16) | 1U); } // Process SWO Mode command and prepare response // request: pointer to request data // response: pointer to response data // return: number of bytes in response (lower 16 bits) // number of bytes in request (upper 16 bits) uint32_t SWO_Mode(const uint8_t *request, uint8_t *response) { uint8_t mode; uint32_t result; mode = *request; // disable swo mode switch (TraceMode) { #if (SWO_UART != 0) case DAP_SWO_UART: UART_SWO_Mode(0U); break; #endif default: break; } switch (mode) { case DAP_SWO_OFF: result = 1U; break; #if (SWO_UART != 0) case DAP_SWO_UART: result = UART_SWO_Mode(1U); break; #endif default: result = 0U; break; } // DAP_SWO_OFF -> has error TraceMode = result ? mode : DAP_SWO_OFF; TraceStatus = 0U; *response = result ? DAP_OK : DAP_ERROR; return ((1U << 16) | 1U); } // Process SWO Baudrate command and prepare response // request: pointer to request data // response: pointer to response data // return: number of bytes in response (lower 16 bits) // number of bytes in request (upper 16 bits) uint32_t SWO_Baudrate(const uint8_t *request, uint8_t *response) { uint32_t baudrate; baudrate = (uint32_t)(*(request + 0) << 0) | (uint32_t)(*(request + 1) << 8) | (uint32_t)(*(request + 2) << 16) | (uint32_t)(*(request + 3) << 24); switch (TraceMode) { #if (SWO_UART != 0) case DAP_SWO_UART: baudrate = UART_SWO_Baudrate(baudrate); break; #endif default: baudrate = 0U; break; } if (baudrate == 0U) { TraceStatus = 0U; } *response++ = (uint8_t)(baudrate >> 0); *response++ = (uint8_t)(baudrate >> 8); *response++ = (uint8_t)(baudrate >> 16); *response = (uint8_t)(baudrate >> 24); return ((4U << 16) | 4U); } // Process SWO Control command and prepare response // request: pointer to request data // response: pointer to response data // return: number of bytes in response (lower 16 bits) // number of bytes in request (upper 16 bits) uint32_t SWO_Control(const uint8_t *request, uint8_t *response) { uint8_t active; uint32_t result; active = *request & DAP_SWO_CAPTURE_ACTIVE; if (active != (TraceStatus & DAP_SWO_CAPTURE_ACTIVE)) { // active status: request != now status if (active) // request: active { ClearTrace(); } switch (TraceMode) { #if (SWO_UART != 0) case DAP_SWO_UART: result = UART_SWO_Control(active); break; #endif default: result = 0U; break; } if (result != 0U) { // success done TraceStatus = active; #if (SWO_STREAM != 0) if (TraceTransport == 2U) // Indicates that we use WinUSB for transfer. { xEventGroupSetBits(kSwoThreadEventGroup, SWO_GOT_DATA); } #endif } } else { // request: active but already actived result = 1U; } *response = result ? DAP_OK : DAP_ERROR; return ((1U << 16) | 1U); } // Process SWO Status command and prepare response // response: pointer to response data // return: number of bytes in response uint32_t SWO_Status(uint8_t *response) { uint8_t status; uint32_t count; status = GetTraceStatus(); count = GetTraceCount(); *response++ = status; *response++ = (uint8_t)(count >> 0); *response++ = (uint8_t)(count >> 8); *response++ = (uint8_t)(count >> 16); *response = (uint8_t)(count >> 24); return (5U); } // Process SWO Extended Status command and prepare response // request: pointer to request data // response: pointer to response data // return: number of bytes in response (lower 16 bits) // number of bytes in request (upper 16 bits) uint32_t SWO_ExtendedStatus(const uint8_t *request, uint8_t *response) { uint8_t cmd; uint8_t status; uint32_t count; #if (TIMESTAMP_CLOCK != 0U) uint32_t index; uint32_t tick; #endif uint32_t num; num = 0U; cmd = *request; if (cmd & 0x01U) { status = GetTraceStatus(); *response++ = status; num += 1U; } if (cmd & 0x02U) { count = GetTraceCount(); *response++ = (uint8_t)(count >> 0); *response++ = (uint8_t)(count >> 8); *response++ = (uint8_t)(count >> 16); *response++ = (uint8_t)(count >> 24); num += 4U; } #if (TIMESTAMP_CLOCK != 0U) if (cmd & 0x04U) { do { TraceUpdate = 0U; index = TraceTimestamp.index; tick = TraceTimestamp.tick; vTaskDelay(pdMS_TO_TICKS(10)); } while (TraceUpdate != 0U); *response++ = (uint8_t)(index >> 0); *response++ = (uint8_t)(index >> 8); *response++ = (uint8_t)(index >> 16); *response++ = (uint8_t)(index >> 24); *response++ = (uint8_t)(tick >> 0); *response++ = (uint8_t)(tick >> 8); *response++ = (uint8_t)(tick >> 16); *response++ = (uint8_t)(tick >> 24); num += 4U; } #endif return ((1U << 16) | num); } // Process SWO Data command and prepare response // request: pointer to request data // response: pointer to response data // return: number of bytes in response (lower 16 bits) // number of bytes in request (upper 16 bits) uint32_t SWO_Data(const uint8_t *request, uint8_t *response) { uint8_t status; uint32_t count; uint32_t index; uint32_t n, i; status = GetTraceStatus(); count = GetTraceCount(); // transport 1: use DAP SWO command // transport 2: WinUSB if (TraceTransport == 1U) { n = (uint32_t)(*(request + 0) << 0) | (uint32_t)(*(request + 1) << 8); if (n > (DAP_PACKET_SIZE - 4U)) { n = DAP_PACKET_SIZE - 4U; } if (count > n) { count = n; } } else { // if use winusb, then nothing to do. count = 0U; } *response++ = status; *response++ = (uint8_t)(count >> 0); *response++ = (uint8_t)(count >> 8); if (TraceTransport == 1U) { index = TraceIndexO; for (i = index, n = count; n; n--) { i &= SWO_BUFFER_SIZE - 1U; *response++ = kSwoTraceBuf[i++]; } TraceIndexO = index + count; ResumeTrace(); } return ((2U << 16) | (3U + count)); } #if (SWO_STREAM != 0) // SWO Data Transfer complete callback void SWO_TransferComplete(void) { TraceIndexO += TransferSize; kSwoTransferBusy = 0U; ResumeTrace(); xEventGroupSetBits(kSwoThreadEventGroup, SWO_GOT_DATA); } // SWO Thread void SWO_Thread() { uint32_t flags; uint32_t count; uint32_t index; uint32_t i, n; uint32_t index_i; uint32_t index_o; uint32_t remain_trace_block_size; TickType_t timeout = portMAX_DELAY; kSwoThreadEventGroup = xEventGroupCreate(); for (;;) { /* Here, our event group handles two main types of events: 1. `SWO_GOT_DATA` : The SWO package may be ready to send. 2. `UART_GOT_DATA` : The uart transfer event is complete and we can start reading. Note that the `SWO_ERROR_TIME_OUT` flag is only used for internal processing, only when the status is inactive can it be set. We deal with uart events first. Currently, SWO events are always handled. */ flags = xEventGroupWaitBits(kSwoThreadEventGroup, SWO_GOT_DATA | UART_GOT_DATA | SWO_ERROR_TIME_OUT, pdTRUE, pdFALSE, timeout); if (!(flags & UART_GOT_DATA) && !(flags & SWO_GOT_DATA)) { if (TraceMode != DAP_SWO_OFF && my_uart_get_rx_buffered_data_len(USART_PORT) >= kSWO_read_num) { flags = UART_GOT_DATA; } } if (flags & UART_GOT_DATA) { // The data is still in the uart buffer, // and the data in the uart needs to be read into the SWO buffer my_uart_read_bytes(USART_PORT, &kSwoTraceBuf[kSWO_read_index], kSWO_read_num, pdMS_TO_TICKS(20)); index_o = TraceIndexO; TraceIndexI += TraceBlockSize; index_i = TraceIndexI; #if (TIMESTAMP_CLOCK != 0U) TraceTimestamp.tick = TIMESTAMP_GET(); TraceTimestamp.index = index_i; #endif remain_trace_block_size = TRACE_BLOCK_SIZE - (index_i & (TRACE_BLOCK_SIZE - 1U)); // remain_trace_block_size is the number of bytes we need to read // (to achieve the size of TRACE_BLOCK_SIZE) count = index_i - index_o; // Amount of data that has not been processed yet // (SWO_BUFFER_SIZE-num): the remaining usable length of the buffer after reading this data if (count <= (SWO_BUFFER_SIZE - remain_trace_block_size)) { // index_i % 4096 index_i &= SWO_BUFFER_SIZE - 1U; TraceBlockSize = remain_trace_block_size; my_uart_read_bytes_async_swo(index_i, remain_trace_block_size); //pUSART->Receive(&kSwoTraceBuf[index_i], num); } else { // Not enough buffers TraceStatus = DAP_SWO_CAPTURE_ACTIVE | DAP_SWO_CAPTURE_PAUSED; } TraceUpdate = 1U; #if (SWO_STREAM != 0) if (TraceTransport == 2U) // Indicates that we use WinUSB for transfer. { // unsent length >= remaining length of the USB block //// TODO: may be we can remove this if (count >= (USB_BLOCK_SIZE - (index_o & (USB_BLOCK_SIZE - 1U)))) { flags = SWO_GOT_DATA; } } #endif } // xEventGroupClearBits /* `SWO_GOT_DATA` may be set in the following situations: - The SWO status requested by `SWD_contorl` is different from the current SWO status. - When `SWO_TransferComplete` is called, and there may be potentially unsent data in the buffer. - monitor task believes we need to send data that is already long enough. Note: The processing of the following code segment will always be executed, and should be ensured to be stable enough. TODO: The time interval for executing the code is not guaranteed. */ if (!(TraceStatus & DAP_SWO_CAPTURE_ACTIVE)) { flags = SWO_ERROR_TIME_OUT; timeout = portMAX_DELAY; } else { timeout = pdMS_TO_TICKS(150); } if (kSwoTransferBusy) { continue; } count = GetTraceCount(); if (count != 0U) { index = TraceIndexO & (SWO_BUFFER_SIZE - 1U); n = SWO_BUFFER_SIZE - index; if (count > n) { count = n; } if ((flags & SWO_ERROR_TIME_OUT) == 0) { i = index & (USB_BLOCK_SIZE - 1U); if (i == 0U) { count &= ~(USB_BLOCK_SIZE - 1U); // Take down to the nearest number that is a multiple of USB_BLOCK_SIZE } else { n = USB_BLOCK_SIZE - i; if (count >= n) { count = n; // The number of bytes to be sent exceeds the remain USB block size. } else { count = 0U; // Haven't received a full USB block yet. } } } // Notify that there is data available for transmission if (count != 0U) { TransferSize = count; kSwoTransferBusy = 1U; SWO_QueueTransfer(&kSwoTraceBuf[index], count); // through USB } } } // For all exceptions, we have handled them directly in the interrupt handlers. } #endif /* (SWO_STREAM != 0) */ #endif /* ((SWO_UART != 0) || (SWO_MANCHESTER != 0)) */