diff --git a/src/command.c b/src/command.c index b812bfe9..1efae36f 100644 --- a/src/command.c +++ b/src/command.c @@ -37,6 +37,8 @@ #include "adiv5.h" +#include + 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 79e4316d..8bc5d353 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 962bd06f..81c1481f 100644 --- a/src/stm32/cdcacm.c +++ b/src/stm32/cdcacm.c @@ -38,12 +38,9 @@ #include #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 00000000..dc659a68 --- /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 + * + * 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 . + */ + +/* 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 +#include +#include + +#include + +#include + +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 00000000..c5bc0e5f --- /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 + * + * 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 . + */ +#ifndef __TRACESWO_H +#define __TRACESWO_H + +void traceswo_init(void); +void trace_buf_drain(uint8_t ep); + +#endif +