SWO support for CMSIS-DAP on the pico (untested as i dont have SWO-capable hardware afaik)

This commit is contained in:
Triss 2021-08-08 18:10:44 +02:00
parent 1e0b016c0a
commit 3143c2d065
6 changed files with 244 additions and 4 deletions

View File

@ -79,6 +79,7 @@ target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/_sump.c ${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/_sump.c
${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/cdc_sump.c ${CMAKE_CURRENT_SOURCE_DIR}/src/m_sump/cdc_sump.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/cdc_uart.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/cdc_uart.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/dap_swo.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/i2c_tinyusb.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/i2c_tinyusb.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/spi_serprog.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/spi_serprog.c
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/tempsensor.c ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/tempsensor.c
@ -95,11 +96,12 @@ target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/libco/ ${CMAKE_CURRENT_SOURCE_DIR}/libco/
${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Include/ ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/DAP/Firmware/Include/
${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/Core/Include/ ${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/Core/Include/
${CMAKE_CURRENT_SOURCE_DIR}/CMSIS_5/CMSIS/Driver/Include/
${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/ ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/
${CMAKE_CURRENT_SOURCE_DIR}/bsp/default/ ${CMAKE_CURRENT_SOURCE_DIR}/bsp/default/
) )
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
add_custom_target(fix_db ALL WORKING_DIRECTORY ${OUTPUT_DIR} add_custom_target(fix_db ALL WORKING_DIRECTORY ${OUTPUT_DIR}
@ -127,6 +129,8 @@ if(FAMILY STREQUAL "rp2040")
target_link_libraries(${PROJECT} pico_stdio) target_link_libraries(${PROJECT} pico_stdio)
endif() endif()
pico_generate_pio_header(${PROJECT} ${CMAKE_CURRENT_SOURCE_DIR}/bsp/${FAMILY}/m_default/uart_rx.pio)
pico_add_extra_outputs(${PROJECT}) pico_add_extra_outputs(${PROJECT})
else() else()

View File

@ -95,6 +95,10 @@ projects. These respective licenses can be found in
supports 7-bit ones). supports 7-bit ones).
- [ ] **1-wire** - [ ] **1-wire**
- [ ] **make modes persistent?** - [ ] **make modes persistent?**
- [ ] CMSIS-DAP SWO support?
- ~~Needs edits in the CMSIS-DAP code~~ ok no theyre weak symbols
- needs PIO stuff tho
- SWO = TDO !
- [ ] FT2232 emulation mode? - [ ] FT2232 emulation mode?
- watch out, still need a vnd cfg interface! libftdi expects the following stuff: (TODO: acquire detailed protocol description) - watch out, still need a vnd cfg interface! libftdi expects the following stuff: (TODO: acquire detailed protocol description)
- interface 0 ("A"): index 1, epin 0x02, epout 0x81 - interface 0 ("A"): index 1, epin 0x02, epout 0x81

View File

@ -68,6 +68,7 @@ This information includes:
#define PINOUT_SWCLK PINOUT_JTAG_TCK #define PINOUT_SWCLK PINOUT_JTAG_TCK
#define PINOUT_SWDIO PINOUT_JTAG_TMS #define PINOUT_SWDIO PINOUT_JTAG_TMS
#define PINOUT_SWO PINOUT_JTAG_TDO
#define PINOUT_SWCLK_MASK (1UL << PINOUT_SWCLK) #define PINOUT_SWCLK_MASK (1UL << PINOUT_SWCLK)
#define PINOUT_SWDIO_MASK (1UL << PINOUT_SWDIO) #define PINOUT_SWDIO_MASK (1UL << PINOUT_SWDIO)
@ -128,10 +129,14 @@ This information includes:
/// Indicate that UART Serial Wire Output (SWO) trace is available. /// Indicate that UART Serial Wire Output (SWO) trace is available.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>. /// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
#define SWO_UART 0 ///< SWO UART: 1 = available, 0 = not available. #define SWO_UART 1 ///< SWO UART: 1 = available, 0 = not available.
// should cause a linker error, because we're not using a hardware UART on the pico for SWO
#define SWO_UART_DRIVER 9999
/// Maximum SWO UART Baudrate. /// Maximum SWO UART Baudrate.
#define SWO_UART_MAX_BAUDRATE 10000000U ///< SWO UART Maximum Baudrate in Hz. #define SWO_UART_MAX_BAUDRATE 10000000U ///< SWO UART Maximum Baudrate in Hz.
// ^ 10 MHz
/// Indicate that Manchester Serial Wire Output (SWO) trace is available. /// Indicate that Manchester Serial Wire Output (SWO) trace is available.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>. /// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
@ -148,7 +153,7 @@ This information includes:
#define DAP_UART 0 ///< DAP UART: 1 = available, 0 = not available. #define DAP_UART 0 ///< DAP UART: 1 = available, 0 = not available.
/// USART Driver instance number for the UART Communication Port. /// USART Driver instance number for the UART Communication Port.
#define DAP_UART_DRIVER 1 ///< USART Driver instance number (Driver_USART#). #define DAP_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#).
/// UART Receive Buffer Size. /// UART Receive Buffer Size.
#define DAP_UART_RX_BUFFER_SIZE 64U ///< Uart Receive Buffer Size in bytes (must be 2^n). #define DAP_UART_RX_BUFFER_SIZE 64U ///< Uart Receive Buffer Size in bytes (must be 2^n).

View File

@ -0,0 +1,133 @@
// vim: set et:
#include "DAP_config.h"
#include "DAP.h"
#include <hardware/dma.h>
#include <hardware/gpio.h>
#include <hardware/pio.h>
#include <hardware/structs/dma.h>
#include "uart_rx.pio.h"
static uint32_t
swo_baudrate = 115200,
swo_pio_off = ~(uint32_t)0, swo_num = 0;
static int swo_sm = -1, swo_dmach = -1;
static bool mode_enabled = false;
#define SWO_PIO pio1
// Enable or disable SWO Mode (UART)
// enable: enable flag
// return: 1 - Success, 0 - Error
uint32_t SWO_Mode_UART(uint32_t enable) {
if (enable) {
swo_sm = pio_claim_unused_sm(SWO_PIO, false);
if (swo_sm == -1) return 0;
swo_dmach = dma_claim_unused_channel(false);
if (swo_dmach == -1) {
pio_sm_unclaim(SWO_PIO, swo_sm);
swo_sm = -1;
return 0;
}
if (!pio_can_add_program(SWO_PIO, &uart_rx_program)) {
dma_channel_unclaim(swo_dmach);
swo_dmach = -1;
pio_sm_unclaim(SWO_PIO, swo_sm);
swo_sm = -1;
return 0;
}
swo_pio_off = pio_add_program(SWO_PIO, &uart_rx_program);
uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, swo_baudrate);
mode_enabled = true;
} else {
mode_enabled = false;
if (swo_dmach >= 0) {
dma_channel_unclaim(swo_dmach); // ugh why is it "dma_channel_xyz" and "dma_xyz_channel"
swo_dmach = -1;
}
if (swo_sm >= 0) {
pio_sm_set_enabled(SWO_PIO, swo_sm, false);
pio_sm_unclaim(SWO_PIO, swo_sm);
swo_sm = -1;
}
if (~swo_pio_off != 0) {
pio_remove_program(SWO_PIO, &uart_rx_program, swo_pio_off);
swo_pio_off = ~(uint32_t)0;
}
// hi-Z nothing
gpio_set_function(PINOUT_SWO, GPIO_FUNC_NULL);
gpio_set_pulls(PINOUT_SWO, false, false);
}
return 1;
}
// Configure SWO Baudrate (UART)
// baudrate: requested baudrate
// return: actual baudrate or 0 when not configured
uint32_t SWO_Baudrate_UART(uint32_t baudrate) {
if (!mode_enabled) return 0;
uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, baudrate);
return baudrate; // should be ok
}
// Control SWO Capture (UART)
// active: active flag
// return: 1 - Success, 0 - Error
uint32_t SWO_Control_UART(uint32_t active) {
if (!mode_enabled) return 0;
if (active) {
pio_sm_set_enabled(SWO_PIO, swo_sm, true);
dma_channel_start(swo_dmach);
}
else {
dma_channel_abort(swo_dmach);
pio_sm_set_enabled(SWO_PIO, swo_sm, false);
swo_num = 0;
}
return 1;
}
// Start SWO Capture (UART)
// buf: pointer to buffer for capturing
// num: number of bytes to capture
void SWO_Capture_UART(uint8_t* buf, uint32_t num) {
if (!mode_enabled) return;
swo_num = num;
// set up DMA params
dma_channel_config dcfg = dma_channel_get_default_config(swo_dmach);
channel_config_set_read_increment(&dcfg, false);
channel_config_set_write_increment(&dcfg, true);
channel_config_set_dreq(&dcfg, pio_get_dreq(SWO_PIO, swo_sm, false));
channel_config_set_transfer_data_size(&dcfg, DMA_SIZE_8);
dma_channel_configure(swo_dmach, &dcfg, buf, &SWO_PIO->rxf[swo_sm], num, false);
}
// Get SWO Pending Trace Count (UART)
// return: number of pending trace data bytes
uint32_t SWO_GetCount_UART(void) {
// DMA hw decreases transfer_count by one on every transfer, so it contains
// the number of remaining bytes to be received.
// however, CMSIS-DAP wants (badly worded) the number of bytes already
// received
if (!mode_enabled || swo_num == 0) return 0; // not initialized
uint32_t remaining = dma_hw->ch[swo_dmach].transfer_count;
return swo_num - remaining;
}

View File

@ -0,0 +1,94 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.program uart_rx_mini
; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits
; with the correct timing.
; IN pin 0 is mapped to the GPIO used as UART RX.
; Autopush must be enabled, with a threshold of 8.
wait 0 pin 0 ; Wait for start bit
set x, 7 [10] ; Preload bit counter, delay until eye of first data bit
bitloop: ; Loop 8 times
in pins, 1 ; Sample data
jmp x-- bitloop [6] ; Each iteration is 8 cycles
% c-sdk {
#include "hardware/clocks.h"
#include "hardware/gpio.h"
static inline void uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
pio_gpio_init(pio, pin);
gpio_pull_up(pin);
pio_sm_config c = uart_rx_mini_program_get_default_config(offset);
sm_config_set_in_pins(&c, pin); // for WAIT, IN
// Shift to right, autopush enabled
sm_config_set_in_shift(&c, true, true, 8);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}
.program uart_rx
; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
; break conditions more gracefully.
; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
start:
wait 0 pin 0 ; Stall until start bit is asserted
set x, 7 [10] ; Preload bit counter, then delay until halfway through
bitloop: ; the first data bit (12 cycles incl wait, set).
in pins, 1 ; Shift data bit into ISR
jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
jmp pin good_stop ; Check stop bit (should be high)
irq 4 rel ; Either a framing error or a break. Set a sticky flag,
wait 1 pin 0 ; and wait for line to return to idle state.
jmp start ; Don't push data if we didn't see good framing.
good_stop: ; No delay before returning to start; a little slack is
push ; important in case the TX clock is slightly too fast.
% c-sdk {
static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) {
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
pio_gpio_init(pio, pin);
gpio_pull_up(pin);
pio_sm_config c = uart_rx_program_get_default_config(offset);
sm_config_set_in_pins(&c, pin); // for WAIT, IN
sm_config_set_jmp_pin(&c, pin); // for JMP
// Shift to right, autopull disabled
sm_config_set_in_shift(&c, true, false, 32);
// Deeper FIFO as we're not doing any TX
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * baud);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
//pio_sm_set_enabled(pio, sm, true);
}
static inline char uart_rx_program_getc(PIO pio, uint sm) {
// 8-bit read from the uppermost byte of the FIFO, as data is left-justified
io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3;
while (pio_sm_is_rx_fifo_empty(pio, sm))
tight_loop_contents();
return (char)*rxfifo_shift;
}
%}

View File

@ -5,7 +5,7 @@
#include <libco.h> #include <libco.h>
#define THREAD_STACK_SIZE 1024 #define THREAD_STACK_SIZE 512
void thread_init (void); void thread_init (void);
void thread_yield(void); void thread_yield(void);