From 541861e978ba88c80e7e2a0cbcd7ce55d574c97b Mon Sep 17 00:00:00 2001 From: Koen De Vleeschauwer Date: Thu, 14 May 2020 13:01:48 +0200 Subject: [PATCH] traceswo decoding --- README.md | 3 ++ src/command.c | 56 ++++++++++++++++----- src/platforms/common/traceswo.h | 13 ++++- src/platforms/f4discovery/Makefile.inc | 1 + src/platforms/hydrabus/Makefile.inc | 1 + src/platforms/native/Makefile.inc | 1 + src/platforms/stlink/Makefile.inc | 1 + src/platforms/stm32/traceswo.c | 17 +++++-- src/platforms/stm32/traceswoasync.c | 26 +++++++--- src/platforms/stm32/traceswodecode.c | 69 ++++++++++++++++++++++++++ src/platforms/swlink/Makefile.inc | 1 + 11 files changed, 164 insertions(+), 25 deletions(-) create mode 100644 src/platforms/stm32/traceswodecode.c diff --git a/README.md b/README.md index 21be638..14db294 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ JTAG or Serial Wire Debugging (SWD) port and on-chip debug logic provided by the microprocessor. The probe connects to a host computer using a standard USB interface. The user is able to control exactly what happens using the GNU source level debugging software, GDB. +Serial Wire Output (SWO) allows the target to write tracing and logging to the host +without using usb or serial port. Decoding SWO in the probe itself +makes [SWO viewing as simple as connecting to a serial port](https://github.com/blacksphere/blackmagic/wiki/Serial-Wire-Output). See online documentation at https://github.com/blacksphere/blackmagic/wiki diff --git a/src/command.c b/src/command.c index dda2e8c..1059e33 100644 --- a/src/command.c +++ b/src/command.c @@ -79,9 +79,9 @@ const struct command_s cmd_list[] = { #endif #ifdef PLATFORM_HAS_TRACESWO #if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2 - {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate)" }, + {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, NRZ mode: (baudrate) (decode channel ...)" }, #else - {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, Manchester mode" }, + {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture, Manchester mode: (decode channel ...)" }, #endif #endif {"heapinfo", (cmd_handler)cmd_heapinfo, "Set semihosting heapinfo" }, @@ -363,19 +363,49 @@ static bool cmd_traceswo(target *t, int argc, const char **argv) extern char serial_no[9]; #endif (void)t; -#if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2 - if (argc > 1) { - uint32_t baudrate = atoi(argv[1]); - traceswo_init(baudrate); - } else { - gdb_outf("Missing baudrate parameter in command\n"); +#if TRACESWO_PROTOCOL == 2 + uint32_t baudrate = SWO_DEFAULT_BAUD; +#endif + uint32_t swo_channelmask = 0; /* swo decoding off */ + uint8_t decode_arg = 1; +#if TRACESWO_PROTOCOL == 2 + /* argument: optional baud rate for async mode */ + if ((argc > 1) && (*argv[1] >= '0') && (*argv[1] <= '9')) { + baudrate = atoi(argv[1]); + if (baudrate == 0) baudrate = SWO_DEFAULT_BAUD; + decode_arg = 2; } +#endif + /* argument: 'decode' literal */ + if((argc > decode_arg) && !strncmp(argv[decode_arg], "decode", strlen(argv[decode_arg]))) { + swo_channelmask = 0xFFFFFFFF; /* decoding all channels */ + /* arguments: channels to decode */ + if (argc > decode_arg + 1) { + swo_channelmask = 0x0; + for (int i = decode_arg+1; i < argc; i++) { /* create bitmask of channels to decode */ + int channel = atoi(argv[i]); + if ((channel >= 0) && (channel <= 31)) + swo_channelmask |= (uint32_t)0x1 << channel; + } + } + } +#if defined(PLATFORM_HAS_DEBUG) && !defined(PC_HOSTED) && defined(ENABLE_DEBUG) + if (debug_bmp) { +#if TRACESWO_PROTOCOL == 2 + gdb_outf("baudrate: %lu ", baudrate); +#endif + gdb_outf("channel mask: "); + for (int8_t i=31;i>=0;i--) { + uint8_t bit = (swo_channelmask >> i) & 0x1; + gdb_outf("%u", bit); + } + gdb_outf("\n"); + } +#endif +#if TRACESWO_PROTOCOL == 2 + traceswo_init(baudrate, swo_channelmask); #else - (void)argv; - traceswo_init(); - if (argc > 1) { - gdb_outf("Superfluous parameter(s) ignored\n"); - } + traceswo_init(swo_channelmask); #endif gdb_outf("%s:%02X:%02X\n", serial_no, 5, 0x85); return true; diff --git a/src/platforms/common/traceswo.h b/src/platforms/common/traceswo.h index 50c9578..4416660 100644 --- a/src/platforms/common/traceswo.h +++ b/src/platforms/common/traceswo.h @@ -23,11 +23,20 @@ #include #if defined TRACESWO_PROTOCOL && TRACESWO_PROTOCOL == 2 -void traceswo_init(uint32_t baudrate); +/* Default line rate, used as default for a request without baudrate */ +#define SWO_DEFAULT_BAUD (2250000) +void traceswo_init(uint32_t baudrate, uint32_t swo_chan_bitmask); #else -void traceswo_init(void); +void traceswo_init(uint32_t swo_chan_bitmask); #endif void trace_buf_drain(usbd_device *dev, uint8_t ep); +/* set bitmask of swo channels to be decoded */ +void traceswo_setmask(uint32_t mask); + +/* print decoded swo packet on usb serial */ +uint16_t traceswo_decode(usbd_device *usbd_dev, uint8_t addr, + const void *buf, uint16_t len); + #endif diff --git a/src/platforms/f4discovery/Makefile.inc b/src/platforms/f4discovery/Makefile.inc index b36bf8f..e1f22e2 100644 --- a/src/platforms/f4discovery/Makefile.inc +++ b/src/platforms/f4discovery/Makefile.inc @@ -16,6 +16,7 @@ LDFLAGS = -lopencm3_stm32f4 \ VPATH += platforms/stm32 SRC += cdcacm.c \ + traceswodecode.c \ traceswo.c \ usbuart.c \ serialno.c \ diff --git a/src/platforms/hydrabus/Makefile.inc b/src/platforms/hydrabus/Makefile.inc index 1c2bf7d..ac96637 100644 --- a/src/platforms/hydrabus/Makefile.inc +++ b/src/platforms/hydrabus/Makefile.inc @@ -16,6 +16,7 @@ LDFLAGS = -lopencm3_stm32f4 \ VPATH += platforms/stm32 SRC += cdcacm.c \ + traceswodecode.c \ traceswo.c \ usbuart.c \ serialno.c \ diff --git a/src/platforms/native/Makefile.inc b/src/platforms/native/Makefile.inc index 1055570..d3cef33 100644 --- a/src/platforms/native/Makefile.inc +++ b/src/platforms/native/Makefile.inc @@ -21,6 +21,7 @@ endif VPATH += platforms/stm32 SRC += cdcacm.c \ + traceswodecode.c \ traceswo.c \ usbuart.c \ serialno.c \ diff --git a/src/platforms/stlink/Makefile.inc b/src/platforms/stlink/Makefile.inc index 32355c3..a0cf7de 100644 --- a/src/platforms/stlink/Makefile.inc +++ b/src/platforms/stlink/Makefile.inc @@ -32,6 +32,7 @@ SRC += cdcacm.c \ serialno.c \ timing.c \ timing_stm32.c \ + traceswodecode.c \ traceswoasync.c \ stlink_common.c \ diff --git a/src/platforms/stm32/traceswo.c b/src/platforms/stm32/traceswo.c index 7af7ef3..1012694 100644 --- a/src/platforms/stm32/traceswo.c +++ b/src/platforms/stm32/traceswo.c @@ -39,7 +39,10 @@ #include #include -void traceswo_init(void) +/* SWO decoding */ +static bool decoding = false; + +void traceswo_init(uint32_t swo_chan_bitmask) { TRACE_TIM_CLK_EN(); @@ -73,6 +76,9 @@ void traceswo_init(void) timer_ic_enable(TRACE_TIM, TIM_IC2); timer_enable_counter(TRACE_TIM); + + traceswo_setmask(swo_chan_bitmask); + decoding = (swo_chan_bitmask != 0); } static uint8_t trace_usb_buf[64]; @@ -80,7 +86,9 @@ static uint8_t trace_usb_buf_size; void trace_buf_push(uint8_t *buf, int len) { - if (usbd_ep_write_packet(usbdev, 0x85, buf, len) != len) { + if (decoding) + traceswo_decode(usbdev, CDCACM_UART_ENDPOINT, buf, len); + else if (usbd_ep_write_packet(usbdev, 0x85, buf, len) != len) { if (trace_usb_buf_size + len > 64) { /* Stall if upstream to too slow. */ usbd_ep_stall_set(usbdev, 0x85, 1); @@ -97,7 +105,10 @@ void trace_buf_drain(usbd_device *dev, uint8_t ep) if (!trace_usb_buf_size) return; - usbd_ep_write_packet(dev, ep, trace_usb_buf, trace_usb_buf_size); + if (decoding) + traceswo_decode(dev, CDCACM_UART_ENDPOINT, trace_usb_buf, trace_usb_buf_size); + else + usbd_ep_write_packet(dev, ep, trace_usb_buf, trace_usb_buf_size); trace_usb_buf_size = 0; } diff --git a/src/platforms/stm32/traceswoasync.c b/src/platforms/stm32/traceswoasync.c index 94b07c3..83362cd 100644 --- a/src/platforms/stm32/traceswoasync.c +++ b/src/platforms/stm32/traceswoasync.c @@ -41,8 +41,6 @@ /* For speed this is set to the USB transfer size */ #define FULL_SWO_PACKET (64) -/* Default line rate....used as default for a request without baudrate */ -#define DEFAULTSPEED (2250000) static volatile uint32_t w; /* Packet currently received via UART */ static volatile uint32_t r; /* Packet currently waiting to transmit to USB */ @@ -50,6 +48,8 @@ static volatile uint32_t r; /* Packet currently waiting to transmit to USB */ static uint8_t trace_rx_buf[NUM_TRACE_PACKETS * FULL_SWO_PACKET]; /* Packet pingpong buffer used for receiving packets */ static uint8_t pingpong_buf[2 * FULL_SWO_PACKET]; +/* SWO decoding */ +static bool decoding = false; void trace_buf_drain(usbd_device *dev, uint8_t ep) { @@ -59,10 +59,20 @@ void trace_buf_drain(usbd_device *dev, uint8_t ep) if (__atomic_test_and_set (&inBufDrain, __ATOMIC_RELAXED)) return; /* Attempt to write everything we buffered */ - if ((w != r) && (usbd_ep_write_packet(dev, ep, + if (w != r) { + uint16_t rc; + if (decoding) + /* write decoded swo packets to the uart port */ + rc = traceswo_decode(dev, CDCACM_UART_ENDPOINT, &trace_rx_buf[r * FULL_SWO_PACKET], - FULL_SWO_PACKET))) - r =(r + 1) % NUM_TRACE_PACKETS; + FULL_SWO_PACKET); + else + /* write raw swo packets to the trace port */ + rc = usbd_ep_write_packet(dev, ep, + &trace_rx_buf[r * FULL_SWO_PACKET], + FULL_SWO_PACKET); + if (rc) r = (r + 1) % NUM_TRACE_PACKETS; + } __atomic_clear (&inBufDrain, __ATOMIC_RELAXED); } @@ -115,10 +125,10 @@ void SWO_DMA_ISR(void) trace_buf_drain(usbdev, 0x85); } -void traceswo_init(uint32_t baudrate) +void traceswo_init(uint32_t baudrate, uint32_t swo_chan_bitmask) { if (!baudrate) - baudrate = DEFAULTSPEED; + baudrate = SWO_DEFAULT_BAUD; rcc_periph_clock_enable(SWO_UART_CLK); rcc_periph_clock_enable(SWO_DMA_CLK); @@ -130,4 +140,6 @@ void traceswo_init(uint32_t baudrate) nvic_set_priority(SWO_DMA_IRQ, IRQ_PRI_SWO_DMA); nvic_enable_irq(SWO_DMA_IRQ); traceswo_setspeed(baudrate); + traceswo_setmask(swo_chan_bitmask); + decoding = (swo_chan_bitmask != 0); } diff --git a/src/platforms/stm32/traceswodecode.c b/src/platforms/stm32/traceswodecode.c new file mode 100644 index 0000000..3d5b052 --- /dev/null +++ b/src/platforms/stm32/traceswodecode.c @@ -0,0 +1,69 @@ +/* + * This file is part of the Black Magic Debug project. + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Print decoded swo stream on the usb serial */ + +#include "general.h" +#include "cdcacm.h" +#include "traceswo.h" + +/* SWO decoding */ +/* data is static in case swo packet is astride two buffers */ +static uint8_t swo_buf[CDCACM_PACKET_SIZE]; +static int swo_buf_len = 0; +static uint32_t swo_decode = 0; /* bitmask of channels to print */ +static int swo_pkt_len = 0; /* decoder state */ +static bool swo_print = false; + +/* print decoded swo packet on usb serial */ +uint16_t traceswo_decode(usbd_device *usbd_dev, uint8_t addr, + const void *buf, uint16_t len) { + if (usbd_dev == NULL) return 0; + for (int i = 0; i> 3; /* channel number */ + uint32_t size = ch & 0x7; /* drop channel number */ + if (size == 0x01) swo_pkt_len = 1; /* SWO packet 0x01XX */ + else if (size == 0x02) swo_pkt_len = 2; /* SWO packet 0x02XXXX */ + else if (size == 0x03) swo_pkt_len = 4; /* SWO packet 0x03XXXXXXXX */ + swo_print = (swo_pkt_len != 0) && ((swo_decode & (1UL << channel)) != 0UL); + } else if (swo_pkt_len <= 4) { /* data */ + if (swo_print) { + swo_buf[swo_buf_len++]=ch; + if (swo_buf_len == sizeof(swo_buf)) { + if (cdcacm_get_config() && cdcacm_get_dtr()) /* silently drop if usb not ready */ + usbd_ep_write_packet(usbd_dev, addr, swo_buf, swo_buf_len); + swo_buf_len=0; + } + } + --swo_pkt_len; + } else { /* recover */ + swo_buf_len=0; + swo_pkt_len=0; + } + } + return len; +} + +/* set bitmask of swo channels to be decoded */ +void traceswo_setmask(uint32_t mask) { + swo_decode = mask; +} + +/* not truncated */ diff --git a/src/platforms/swlink/Makefile.inc b/src/platforms/swlink/Makefile.inc index 9c4394d..b3831f7 100644 --- a/src/platforms/swlink/Makefile.inc +++ b/src/platforms/swlink/Makefile.inc @@ -25,6 +25,7 @@ SRC += cdcacm.c \ serialno.c \ timing.c \ timing_stm32.c \ + traceswodecode.c \ traceswoasync.c \ platform_common.c \