Added capture of SWO, passed to host over a vendor specific USB interface.
This commit is contained in:
parent
9cff48dc6b
commit
21ecbd23ea
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000
|
|||
|
||||
SRC += cdcacm.c \
|
||||
platform.c \
|
||||
traceswo.c \
|
||||
|
||||
all: blackmagic.bin blackmagic_dfu.bin
|
||||
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue