Added capture of SWO, passed to host over a vendor specific USB interface.

This commit is contained in:
Gareth McMullin 2012-04-17 21:40:39 +12:00
parent 9cff48dc6b
commit 21ecbd23ea
5 changed files with 253 additions and 22 deletions

View File

@ -37,6 +37,8 @@
#include "adiv5.h" #include "adiv5.h"
#include <libopencm3/usb/usbd.h>
static void cmd_version(void); static void cmd_version(void);
static void cmd_help(void); static void cmd_help(void);
@ -45,6 +47,8 @@ static void cmd_swdp_scan(void);
static void cmd_targets(void); static void cmd_targets(void);
static void cmd_morse(void); static void cmd_morse(void);
static void cmd_traceswo(void);
const struct command_s cmd_list[] = { const struct command_s cmd_list[] = {
{"version", (cmd_handler)cmd_version, "Display firmware version info"}, {"version", (cmd_handler)cmd_version, "Display firmware version info"},
{"help", (cmd_handler)cmd_help, "Display help for monitor commands"}, {"help", (cmd_handler)cmd_help, "Display help for monitor commands"},
@ -52,6 +56,7 @@ const struct command_s cmd_list[] = {
{"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" }, {"swdp_scan", (cmd_handler)cmd_swdp_scan, "Scan SW-DP for devices" },
{"targets", (cmd_handler)cmd_targets, "Display list of available targets" }, {"targets", (cmd_handler)cmd_targets, "Display list of available targets" },
{"morse", (cmd_handler)cmd_morse, "Display morse error message" }, {"morse", (cmd_handler)cmd_morse, "Display morse error message" },
{"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture" },
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };
@ -159,4 +164,8 @@ void cmd_morse(void)
gdb_outf("%s\n", morse_msg); gdb_outf("%s\n", morse_msg);
} }
static void cmd_traceswo(void)
{
traceswo_init();
}

View File

@ -11,6 +11,7 @@ LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000
SRC += cdcacm.c \ SRC += cdcacm.c \
platform.c \ platform.c \
traceswo.c \
all: blackmagic.bin blackmagic_dfu.bin all: blackmagic.bin blackmagic_dfu.bin

View File

@ -38,12 +38,9 @@
#include <stdlib.h> #include <stdlib.h>
#include "platform.h" #include "platform.h"
#include "traceswo.h"
#ifdef INCLUDE_UART_INTERFACE #define DFU_IF_NO 4
# define DFU_IF_NO 4
#else
# define DFU_IF_NO 2
#endif
static char *get_dev_unique_id(char *serial_no); static char *get_dev_unique_id(char *serial_no);
@ -172,7 +169,6 @@ static const struct usb_iface_assoc_descriptor gdb_assoc = {
.iFunction = 0, .iFunction = 0,
}; };
#ifdef INCLUDE_UART_INTERFACE
/* Serial ACM interface */ /* Serial ACM interface */
static const struct usb_endpoint_descriptor uart_comm_endp[] = {{ static const struct usb_endpoint_descriptor uart_comm_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE, .bLength = USB_DT_ENDPOINT_SIZE,
@ -275,7 +271,6 @@ static const struct usb_iface_assoc_descriptor uart_assoc = {
.bFunctionProtocol = USB_CDC_PROTOCOL_AT, .bFunctionProtocol = USB_CDC_PROTOCOL_AT,
.iFunction = 0, .iFunction = 0,
}; };
#endif
const struct usb_dfu_descriptor dfu_function = { const struct usb_dfu_descriptor dfu_function = {
.bLength = sizeof(struct usb_dfu_descriptor), .bLength = sizeof(struct usb_dfu_descriptor),
@ -312,6 +307,40 @@ static const struct usb_iface_assoc_descriptor dfu_assoc = {
.iFunction = 6, .iFunction = 6,
}; };
static const struct usb_endpoint_descriptor trace_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x85,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 16,
.bInterval = 0,
}};
const struct usb_interface_descriptor trace_iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 5,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0xFF,
.bInterfaceProtocol = 0xFF,
.iInterface = 7,
.endpoint = trace_endp,
};
static const struct usb_iface_assoc_descriptor trace_assoc = {
.bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 5,
.bInterfaceCount = 1,
.bFunctionClass = 0xFF,
.bFunctionSubClass = 0xFF,
.bFunctionProtocol = 0xFF,
.iFunction = 7,
};
static const struct usb_interface ifaces[] = {{ static const struct usb_interface ifaces[] = {{
.num_altsetting = 1, .num_altsetting = 1,
.iface_assoc = &gdb_assoc, .iface_assoc = &gdb_assoc,
@ -320,7 +349,6 @@ static const struct usb_interface ifaces[] = {{
.num_altsetting = 1, .num_altsetting = 1,
.altsetting = gdb_data_iface, .altsetting = gdb_data_iface,
}, { }, {
#ifdef INCLUDE_UART_INTERFACE
.num_altsetting = 1, .num_altsetting = 1,
.iface_assoc = &uart_assoc, .iface_assoc = &uart_assoc,
.altsetting = uart_comm_iface, .altsetting = uart_comm_iface,
@ -328,21 +356,20 @@ static const struct usb_interface ifaces[] = {{
.num_altsetting = 1, .num_altsetting = 1,
.altsetting = uart_data_iface, .altsetting = uart_data_iface,
}, { }, {
#endif
.num_altsetting = 1, .num_altsetting = 1,
.iface_assoc = &dfu_assoc, .iface_assoc = &dfu_assoc,
.altsetting = &dfu_iface, .altsetting = &dfu_iface,
}, {
.num_altsetting = 1,
.iface_assoc = &trace_assoc,
.altsetting = &trace_iface,
}}; }};
static const struct usb_config_descriptor config = { static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE, .bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION, .bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0, .wTotalLength = 0,
#ifdef INCLUDE_UART_INTERFACE .bNumInterfaces = 6,
.bNumInterfaces = 5,
#else
.bNumInterfaces = 3,
#endif
.bConfigurationValue = 1, .bConfigurationValue = 1,
.iConfiguration = 0, .iConfiguration = 0,
.bmAttributes = 0x80, .bmAttributes = 0x80,
@ -361,6 +388,7 @@ static const char *usb_strings[] = {
"Black Magic GDB Server", "Black Magic GDB Server",
"Black Magic UART Port", "Black Magic UART Port",
"Black Magic Firmware Upgrade", "Black Magic Firmware Upgrade",
"Black Magic Trace Capture",
}; };
static void dfu_detach_complete(struct usb_setup_data *req) static void dfu_detach_complete(struct usb_setup_data *req)
@ -395,7 +423,6 @@ static int cdcacm_control_request(struct usb_setup_data *req, uint8_t **buf,
cdcacm_gdb_dtr = req->wValue & 1; cdcacm_gdb_dtr = req->wValue & 1;
return 1; return 1;
#ifdef INCLUDE_UART_INTERFACE
case USB_CDC_REQ_SET_LINE_CODING: { case USB_CDC_REQ_SET_LINE_CODING: {
if(*len < sizeof(struct usb_cdc_line_coding)) if(*len < sizeof(struct usb_cdc_line_coding))
return 0; return 0;
@ -434,7 +461,6 @@ static int cdcacm_control_request(struct usb_setup_data *req, uint8_t **buf,
return 1; return 1;
} }
#endif
case DFU_GETSTATUS: case DFU_GETSTATUS:
if(req->wIndex == DFU_IF_NO) { if(req->wIndex == DFU_IF_NO) {
(*buf)[0] = DFU_STATUS_OK; (*buf)[0] = DFU_STATUS_OK;
@ -467,7 +493,6 @@ int cdcacm_get_dtr(void)
return cdcacm_gdb_dtr; return cdcacm_gdb_dtr;
} }
#ifdef INCLUDE_UART_INTERFACE
static void cdcacm_data_rx_cb(u8 ep) static void cdcacm_data_rx_cb(u8 ep)
{ {
(void)ep; (void)ep;
@ -477,7 +502,6 @@ static void cdcacm_data_rx_cb(u8 ep)
for(int i = 0; i < len; i++) for(int i = 0; i < len; i++)
usart_send_blocking(USART1, buf[i]); usart_send_blocking(USART1, buf[i]);
} }
#endif
static void cdcacm_set_config(u16 wValue) static void cdcacm_set_config(u16 wValue)
{ {
@ -488,12 +512,13 @@ static void cdcacm_set_config(u16 wValue)
usbd_ep_setup(0x81, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); usbd_ep_setup(0x81, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL);
usbd_ep_setup(0x82, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); usbd_ep_setup(0x82, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
#ifdef INCLUDE_UART_INTERFACE
/* Serial interface */ /* Serial interface */
usbd_ep_setup(0x03, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, cdcacm_data_rx_cb); usbd_ep_setup(0x03, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, cdcacm_data_rx_cb);
usbd_ep_setup(0x83, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); usbd_ep_setup(0x83, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL);
usbd_ep_setup(0x84, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); usbd_ep_setup(0x84, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
#endif
/* Trace interface */
usbd_ep_setup(0x85, USB_ENDPOINT_ATTR_BULK, 16, trace_buf_drain);
usbd_register_control_callback( usbd_register_control_callback(
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
@ -514,10 +539,8 @@ static void cdcacm_set_config(u16 wValue)
buf[8] = 3; /* DCD | DSR */ buf[8] = 3; /* DCD | DSR */
buf[9] = 0; buf[9] = 0;
usbd_ep_write_packet(0x82, buf, 10); usbd_ep_write_packet(0x82, buf, 10);
#ifdef INCLUDE_UART_INTERFACE
notif->wIndex = 2; notif->wIndex = 2;
usbd_ep_write_packet(0x84, buf, 10); usbd_ep_write_packet(0x84, buf, 10);
#endif
} }
/* We need a special large control buffer for this device: */ /* We need a special large control buffer for this device: */

171
src/stm32/traceswo.c Normal file
View File

@ -0,0 +1,171 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2012 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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/>.
*/
/* This file implements capture of the TRACESWO output.
*
* ARM DDI 0403D - ARMv7M Architecture Reference Manual
* ARM DDI 0337I - Cortex-M3 Technical Reference Manual
* ARM DDI 0314H - CoreSight Components Technical Reference Manual
*/
/* TDO/TRACESWO signal comes into pin PA6/TIM3_CH1
* Manchester coding is assumed on TRACESWO, so bit timing can be detected.
* The idea is to use TIM3 input capture modes to capture pulse timings.
* These can be capture directly to RAM by DMA.
* The core can then process the buffer to extract the frame.
*/
#include <libopencm3/stm32/nvic.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/f1/rcc.h>
#include <libopencm3/usb/usbd.h>
#include <string.h>
void traceswo_init(void)
{
rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM3EN);
timer_reset(TIM3);
/* Refer to ST doc RM0008 - STM32F10xx Reference Manual.
* Section 14.3.4 - 14.3.6 (General Purpose Timer - Input Capture)
*
* CCR1 captures cycle time, CCR2 captures high time
*/
/* Use TI1 as capture input for CH1 and CH2 */
timer_ic_set_input(TIM3, TIM_IC1, TIM_IC_IN_TI1);
timer_ic_set_input(TIM3, TIM_IC2, TIM_IC_IN_TI1);
/* Capture CH1 on rising edge, CH2 on falling edge */
timer_ic_set_polarity(TIM3, TIM_IC1, TIM_IC_RISING);
timer_ic_set_polarity(TIM3, TIM_IC2, TIM_IC_FALLING);
/* Trigger on Filtered Timer Input 1 (TI1FP1) */
timer_slave_set_trigger(TIM3, TIM_SMCR_TS_IT1FP1);
/* Slave reset mode: reset counter on trigger */
timer_slave_set_mode(TIM3, TIM_SMCR_SMS_RM);
/* Enable capture interrupt */
nvic_enable_irq(NVIC_TIM3_IRQ);
timer_enable_irq(TIM3, TIM_DIER_CC1IE);
/* Enable the capture channels */
timer_ic_enable(TIM3, TIM_IC1);
timer_ic_enable(TIM3, TIM_IC2);
timer_enable_counter(TIM3);
}
static uint8_t trace_usb_buf[16];
static uint8_t trace_usb_buf_size;
void trace_buf_push(uint8_t *buf, int len)
{
if (usbd_ep_write_packet(0x85, buf, len) != len) {
memcpy(trace_usb_buf, buf, len);
trace_usb_buf_size = len;
}
}
void trace_buf_drain(uint8_t ep)
{
if (!trace_usb_buf_size)
return;
usbd_ep_write_packet(ep, trace_usb_buf, trace_usb_buf_size);
trace_usb_buf_size = 0;
}
void tim3_isr(void)
{
uint16_t sr = TIM_SR(TIM3) & TIM_DIER(TIM3);
uint16_t duty, cycle;
static uint16_t bt;
static uint8_t lastbit;
static uint8_t decbuf[17];
static uint8_t decbuf_pos;
/* Reset decoder state if capture overflowed */
if (sr & (TIM_SR_CC1OF | TIM_SR_UIF)) {
timer_clear_flag(TIM3, TIM_SR_CC1OF | TIM_SR_UIF);
if (!(sr & TIM_SR_CC1IF)) {
trace_buf_push(decbuf, decbuf_pos >> 3);
memset(decbuf, 0, sizeof(decbuf));
decbuf_pos = 0;
bt = 0;
timer_set_period(TIM3, -1);
timer_disable_irq(TIM3, TIM_DIER_UIE);
return;
}
}
if (!(sr & TIM_SR_CC1IF))
return;
cycle = TIM_CCR1(TIM3);
duty = TIM_CCR2(TIM3);
/* Reset decoder state if crazy shit happened */
if ((bt && (((duty / bt) > 2) || ((cycle / bt) > 4))) ||
(duty == 0)) {
bt = 0;
trace_buf_push(decbuf, decbuf_pos >> 3);
decbuf_pos = 0;
memset(decbuf, 0, sizeof(decbuf));
return;
}
if (!bt) {
/* First bit, sync decoder */
if ((cycle / (duty - 5)) != 2)
return;
bt = duty - 5;
lastbit = 1;
timer_set_period(TIM3, duty * 5);
timer_clear_flag(TIM3, TIM_SR_UIF);
timer_enable_irq(TIM3, TIM_DIER_UIE);
} else {
/* If high time is extended we need to flip the bit */
if ((duty / bt) > 1)
lastbit ^= 1;
decbuf[decbuf_pos >> 3] |= lastbit << (decbuf_pos & 7);
decbuf_pos++;
}
if (((cycle - duty) / bt) > 1) {
/* If low time extended we need to pack another bit. */
lastbit ^= 1;
decbuf[decbuf_pos >> 3] |= lastbit << (decbuf_pos & 7);
decbuf_pos++;
}
if (decbuf_pos >= 128) {
trace_buf_push(decbuf, 16);
/* bt = 0; */
decbuf_pos = 0;
memset(decbuf, 0, sizeof(decbuf));
}
}

27
src/stm32/traceswo.h Normal file
View File

@ -0,0 +1,27 @@
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2012 Black Sphere Technologies Ltd.
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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/>.
*/
#ifndef __TRACESWO_H
#define __TRACESWO_H
void traceswo_init(void);
void trace_buf_drain(uint8_t ep);
#endif