traceswo decoding

This commit is contained in:
Koen De Vleeschauwer 2020-05-14 13:01:48 +02:00 committed by UweBonnes
parent 54ee00b0f6
commit 541861e978
11 changed files with 164 additions and 25 deletions

View File

@ -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

View File

@ -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;

View File

@ -23,11 +23,20 @@
#include <libopencm3/usb/usbd.h>
#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

View File

@ -16,6 +16,7 @@ LDFLAGS = -lopencm3_stm32f4 \
VPATH += platforms/stm32
SRC += cdcacm.c \
traceswodecode.c \
traceswo.c \
usbuart.c \
serialno.c \

View File

@ -16,6 +16,7 @@ LDFLAGS = -lopencm3_stm32f4 \
VPATH += platforms/stm32
SRC += cdcacm.c \
traceswodecode.c \
traceswo.c \
usbuart.c \
serialno.c \

View File

@ -21,6 +21,7 @@ endif
VPATH += platforms/stm32
SRC += cdcacm.c \
traceswodecode.c \
traceswo.c \
usbuart.c \
serialno.c \

View File

@ -32,6 +32,7 @@ SRC += cdcacm.c \
serialno.c \
timing.c \
timing_stm32.c \
traceswodecode.c \
traceswoasync.c \
stlink_common.c \

View File

@ -39,7 +39,10 @@
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/rcc.h>
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;
}

View File

@ -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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/* 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<len; i++) {
uint8_t ch=((uint8_t*)buf)[i];
if (swo_pkt_len == 0) { /* header */
uint32_t channel = (uint32_t)ch >> 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 */

View File

@ -25,6 +25,7 @@ SRC += cdcacm.c \
serialno.c \
timing.c \
timing_stm32.c \
traceswodecode.c \
traceswoasync.c \
platform_common.c \