aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGareth McMullin2012-04-17 21:40:39 +1200
committerGareth McMullin2012-04-18 19:15:44 +1200
commit21ecbd23ea6ea3c3f72ad9c45d9be3e6e8b7c4f7 (patch)
tree3bce8158a70899170b596bd13b16620a580a724f
parent9cff48dc6b065afe60cf696fcff1ebd822bfce3b (diff)
Added capture of SWO, passed to host over a vendor specific USB interface.
-rw-r--r--src/command.c9
-rw-r--r--src/stm32/Makefile.inc1
-rw-r--r--src/stm32/cdcacm.c67
-rw-r--r--src/stm32/traceswo.c171
-rw-r--r--src/stm32/traceswo.h27
5 files changed, 253 insertions, 22 deletions
diff --git a/src/command.c b/src/command.c
index b812bfe..1efae36 100644
--- a/src/command.c
+++ b/src/command.c
@@ -37,6 +37,8 @@
#include "adiv5.h"
+#include <libopencm3/usb/usbd.h>
+
static void cmd_version(void);
static void cmd_help(void);
@@ -45,6 +47,8 @@ static void cmd_swdp_scan(void);
static void cmd_targets(void);
static void cmd_morse(void);
+static void cmd_traceswo(void);
+
const struct command_s cmd_list[] = {
{"version", (cmd_handler)cmd_version, "Display firmware version info"},
{"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" },
{"targets", (cmd_handler)cmd_targets, "Display list of available targets" },
{"morse", (cmd_handler)cmd_morse, "Display morse error message" },
+ {"traceswo", (cmd_handler)cmd_traceswo, "Start trace capture" },
{NULL, NULL, NULL}
};
@@ -159,4 +164,8 @@ void cmd_morse(void)
gdb_outf("%s\n", morse_msg);
}
+static void cmd_traceswo(void)
+{
+ traceswo_init();
+}
diff --git a/src/stm32/Makefile.inc b/src/stm32/Makefile.inc
index 79e4316..8bc5d35 100644
--- a/src/stm32/Makefile.inc
+++ b/src/stm32/Makefile.inc
@@ -11,6 +11,7 @@ LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000
SRC += cdcacm.c \
platform.c \
+ traceswo.c \
all: blackmagic.bin blackmagic_dfu.bin
diff --git a/src/stm32/cdcacm.c b/src/stm32/cdcacm.c
index 962bd06..81c1481 100644
--- a/src/stm32/cdcacm.c
+++ b/src/stm32/cdcacm.c
@@ -38,12 +38,9 @@
#include <stdlib.h>
#include "platform.h"
+#include "traceswo.h"
-#ifdef INCLUDE_UART_INTERFACE
-# define DFU_IF_NO 4
-#else
-# define DFU_IF_NO 2
-#endif
+#define DFU_IF_NO 4
static char *get_dev_unique_id(char *serial_no);
@@ -172,7 +169,6 @@ static const struct usb_iface_assoc_descriptor gdb_assoc = {
.iFunction = 0,
};
-#ifdef INCLUDE_UART_INTERFACE
/* Serial ACM interface */
static const struct usb_endpoint_descriptor uart_comm_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
@@ -275,7 +271,6 @@ static const struct usb_iface_assoc_descriptor uart_assoc = {
.bFunctionProtocol = USB_CDC_PROTOCOL_AT,
.iFunction = 0,
};
-#endif
const struct usb_dfu_descriptor dfu_function = {
.bLength = sizeof(struct usb_dfu_descriptor),
@@ -312,6 +307,40 @@ static const struct usb_iface_assoc_descriptor dfu_assoc = {
.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[] = {{
.num_altsetting = 1,
.iface_assoc = &gdb_assoc,
@@ -320,7 +349,6 @@ static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = gdb_data_iface,
}, {
-#ifdef INCLUDE_UART_INTERFACE
.num_altsetting = 1,
.iface_assoc = &uart_assoc,
.altsetting = uart_comm_iface,
@@ -328,21 +356,20 @@ static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = uart_data_iface,
}, {
-#endif
.num_altsetting = 1,
.iface_assoc = &dfu_assoc,
.altsetting = &dfu_iface,
+}, {
+ .num_altsetting = 1,
+ .iface_assoc = &trace_assoc,
+ .altsetting = &trace_iface,
}};
static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
-#ifdef INCLUDE_UART_INTERFACE
- .bNumInterfaces = 5,
-#else
- .bNumInterfaces = 3,
-#endif
+ .bNumInterfaces = 6,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
@@ -361,6 +388,7 @@ static const char *usb_strings[] = {
"Black Magic GDB Server",
"Black Magic UART Port",
"Black Magic Firmware Upgrade",
+ "Black Magic Trace Capture",
};
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;
return 1;
-#ifdef INCLUDE_UART_INTERFACE
case USB_CDC_REQ_SET_LINE_CODING: {
if(*len < sizeof(struct usb_cdc_line_coding))
return 0;
@@ -434,7 +461,6 @@ static int cdcacm_control_request(struct usb_setup_data *req, uint8_t **buf,
return 1;
}
-#endif
case DFU_GETSTATUS:
if(req->wIndex == DFU_IF_NO) {
(*buf)[0] = DFU_STATUS_OK;
@@ -467,7 +493,6 @@ int cdcacm_get_dtr(void)
return cdcacm_gdb_dtr;
}
-#ifdef INCLUDE_UART_INTERFACE
static void cdcacm_data_rx_cb(u8 ep)
{
(void)ep;
@@ -477,7 +502,6 @@ static void cdcacm_data_rx_cb(u8 ep)
for(int i = 0; i < len; i++)
usart_send_blocking(USART1, buf[i]);
}
-#endif
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(0x82, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL);
-#ifdef INCLUDE_UART_INTERFACE
/* Serial interface */
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(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(
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[9] = 0;
usbd_ep_write_packet(0x82, buf, 10);
-#ifdef INCLUDE_UART_INTERFACE
notif->wIndex = 2;
usbd_ep_write_packet(0x84, buf, 10);
-#endif
}
/* We need a special large control buffer for this device: */
diff --git a/src/stm32/traceswo.c b/src/stm32/traceswo.c
new file mode 100644
index 0000000..dc659a6
--- /dev/null
+++ b/src/stm32/traceswo.c
@@ -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));
+ }
+
+}
+
+
diff --git a/src/stm32/traceswo.h b/src/stm32/traceswo.h
new file mode 100644
index 0000000..c5bc0e5
--- /dev/null
+++ b/src/stm32/traceswo.h
@@ -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
+