From 54f4467c0f9d910517140adae385f4c4f386fafc Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Sun, 10 Jun 2012 19:02:58 +1200 Subject: [PATCH] Added stlink platform for Discovery boards and ST's ST-Link hardware. --- src/platforms/native/Makefile.inc | 4 +- src/platforms/native/platform.c | 3 - src/platforms/stlink/Makefile.inc | 22 ++ src/platforms/stlink/cdcacm.c | 306 ++++++++++++++++++ src/platforms/stlink/platform.c | 96 ++++++ src/platforms/stlink/platform.h | 142 ++++++++ src/platforms/{native => stm32}/blackmagic.ld | 0 src/platforms/stm32/jtagtap.c | 4 + 8 files changed, 572 insertions(+), 5 deletions(-) create mode 100644 src/platforms/stlink/Makefile.inc create mode 100644 src/platforms/stlink/cdcacm.c create mode 100644 src/platforms/stlink/platform.c create mode 100644 src/platforms/stlink/platform.h rename src/platforms/{native => stm32}/blackmagic.ld (100%) diff --git a/src/platforms/native/Makefile.inc b/src/platforms/native/Makefile.inc index b60b562..41e1717 100644 --- a/src/platforms/native/Makefile.inc +++ b/src/platforms/native/Makefile.inc @@ -4,8 +4,8 @@ OBJCOPY = $(CROSS_COMPILE)objcopy CFLAGS += -Istm32/include -mcpu=cortex-m3 -mthumb -DSTM32F1 LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \ - -Wl,-T,$(PLATFORM_DIR)/blackmagic.ld -nostartfiles -lc -lnosys -Wl,-Map=mapfile \ - -mthumb -mcpu=cortex-m3 -Wl,-gc-sections + -Wl,-T,platforms/stm32/blackmagic.ld -nostartfiles -lc -lnosys \ + -Wl,-Map=mapfile -mthumb -mcpu=cortex-m3 -Wl,-gc-sections LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000 VPATH += platforms/stm32 diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c index 0b26272..16ef28f 100644 --- a/src/platforms/native/platform.c +++ b/src/platforms/native/platform.c @@ -41,7 +41,6 @@ volatile uint32_t timeout_counter; jmp_buf fatal_error_jmpbuf; -void morse(const char *msg, char repeat); static void morse_update(void); static void adc_init(void); @@ -69,10 +68,8 @@ int platform_init(void) /* Enable peripherals */ rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); - rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN); rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); - rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPDEN); rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN); /* Setup GPIO ports */ diff --git a/src/platforms/stlink/Makefile.inc b/src/platforms/stlink/Makefile.inc new file mode 100644 index 0000000..7124dfc --- /dev/null +++ b/src/platforms/stlink/Makefile.inc @@ -0,0 +1,22 @@ +CROSS_COMPILE ?= arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS += -mcpu=cortex-m3 -mthumb -DSTM32F1 +LDFLAGS = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \ + -Wl,-T,platforms/stm32/blackmagic.ld -nostartfiles -lc -lnosys \ + -Wl,-Map=mapfile -mthumb -mcpu=cortex-m3 -Wl,-gc-sections + +VPATH += platforms/stm32 + +SRC += cdcacm.c \ + platform.c \ + +all: blackmagic.bin + +blackmagic.bin: blackmagic + $(OBJCOPY) -O binary $^ $@ + +host_clean: + -rm blackmagic.bin + diff --git a/src/platforms/stlink/cdcacm.c b/src/platforms/stlink/cdcacm.c new file mode 100644 index 0000000..db54185 --- /dev/null +++ b/src/platforms/stlink/cdcacm.c @@ -0,0 +1,306 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 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 a the USB Communications Device Class - Abstract + * Control Model (CDC-ACM) as defined in CDC PSTN subclass 1.2. + * A Device Firmware Upgrade (DFU 1.1) class interface is provided for + * field firmware upgrade. + * + * The device's unique id is used as the USB serial number string. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" + +static char *get_dev_unique_id(char *serial_no); + +static int configured; +static int cdcacm_gdb_dtr = 1; + +static const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0xEF, /* Miscellaneous Device */ + .bDeviceSubClass = 2, /* Common Class */ + .bDeviceProtocol = 1, /* Interface Association */ + .bMaxPacketSize0 = 64, + .idVendor = 0x1D50, + .idProduct = 0x6018, + .bcdDevice = 0x0100, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +/* This notification endpoint isn't implemented. According to CDC spec its + * optional, but its absence causes a NULL pointer dereference in Linux cdc_acm + * driver. */ +static const struct usb_endpoint_descriptor gdb_comm_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x82, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 16, + .bInterval = 255, +}}; + +static const struct usb_endpoint_descriptor gdb_data_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x01, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = CDCACM_PACKET_SIZE, + .bInterval = 1, +}, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x81, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = CDCACM_PACKET_SIZE, + .bInterval = 1, +}}; + +static const struct { + struct usb_cdc_header_descriptor header; + struct usb_cdc_call_management_descriptor call_mgmt; + struct usb_cdc_acm_descriptor acm; + struct usb_cdc_union_descriptor cdc_union; +} __attribute__((packed)) gdb_cdcacm_functional_descriptors = { + .header = { + .bFunctionLength = sizeof(struct usb_cdc_header_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_HEADER, + .bcdCDC = 0x0110, + }, + .call_mgmt = { + .bFunctionLength = + sizeof(struct usb_cdc_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, + .bmCapabilities = 0, + .bDataInterface = 1, + }, + .acm = { + .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_ACM, + .bmCapabilities = 2, /* SET_LINE_CODING supported */ + }, + .cdc_union = { + .bFunctionLength = sizeof(struct usb_cdc_union_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_UNION, + .bControlInterface = 0, + .bSubordinateInterface0 = 1, + } +}; + +static const struct usb_interface_descriptor gdb_comm_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, + .iInterface = 4, + + .endpoint = gdb_comm_endp, + + .extra = &gdb_cdcacm_functional_descriptors, + .extralen = sizeof(gdb_cdcacm_functional_descriptors) +}}; + +static const struct usb_interface_descriptor gdb_data_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + + .endpoint = gdb_data_endp, +}}; + +static const struct usb_iface_assoc_descriptor gdb_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 0, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTOCOL_AT, + .iFunction = 0, +}; + +static const struct usb_interface ifaces[] = {{ + .num_altsetting = 1, + .iface_assoc = &gdb_assoc, + .altsetting = gdb_comm_iface, +}, { + .num_altsetting = 1, + .altsetting = gdb_data_iface, +}}; + +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + + .interface = ifaces, +}; + +char serial_no[9]; + +static const char *usb_strings[] = { + "x", + "Black Sphere Technologies", + "Black Magic Probe", + serial_no, + "Black Magic GDB Server", +}; + +static int cdcacm_control_request(struct usb_setup_data *req, uint8_t **buf, + uint16_t *len, void (**complete)(struct usb_setup_data *req)) +{ + (void)complete; + (void)buf; + (void)len; + + switch(req->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + /* Ignore if not for GDB interface */ + if(req->wIndex != 0) + return 1; + + cdcacm_gdb_dtr = req->wValue & 1; + + return 1; + case USB_CDC_REQ_SET_LINE_CODING: + if(*len < sizeof(struct usb_cdc_line_coding)) + return 0; + + switch(req->wIndex) { + case 0: + return 1; /* Ignore on GDB Port */ + default: + return 0; + } + } + return 0; +} + +int cdcacm_get_config(void) +{ + return configured; +} + +int cdcacm_get_dtr(void) +{ + return cdcacm_gdb_dtr; +} + +static void cdcacm_set_config(u16 wValue) +{ + configured = wValue; + + /* GDB interface */ + usbd_ep_setup(0x01, 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_register_control_callback( + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + cdcacm_control_request); + + /* Notify the host that DCD is asserted. + * Allows the use of /dev/tty* devices on *BSD/MacOS + */ + char buf[10]; + struct usb_cdc_notification *notif = (void*)buf; + /* We echo signals back to host as notification */ + notif->bmRequestType = 0xA1; + notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; + notif->wValue = 0; + notif->wIndex = 0; + notif->wLength = 2; + buf[8] = 3; /* DCD | DSR */ + buf[9] = 0; + usbd_ep_write_packet(0x82, buf, 10); +} + +void cdcacm_init(void) +{ + void exti15_10_isr(void); + + get_dev_unique_id(serial_no); + + usbd_init(&stm32f103_usb_driver, &dev, &config, usb_strings); + usbd_register_set_config_callback(cdcacm_set_config); + + nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); +} + +void usb_lp_can_rx0_isr(void) +{ + usbd_poll(); +} + +static char *get_dev_unique_id(char *s) +{ + volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFFF7E8; + uint32_t unique_id = *unique_id_p + + *(unique_id_p + 1) + + *(unique_id_p + 2); + int i; + + /* Fetch serial number from chip's unique ID */ + for(i = 0; i < 8; i++) { + s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0'; + } + for(i = 0; i < 8; i++) + if(s[i] > '9') + s[i] += 'A' - '9' - 1; + s[8] = 0; + + return s; +} + diff --git a/src/platforms/stlink/platform.c b/src/platforms/stlink/platform.c new file mode 100644 index 0000000..777db95 --- /dev/null +++ b/src/platforms/stlink/platform.c @@ -0,0 +1,96 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 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 the platform specific functions for the ST-Link + * implementation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "jtag_scan.h" + +#include + +uint8_t running_status; +volatile uint32_t timeout_counter; + +jmp_buf fatal_error_jmpbuf; + +int platform_init(void) +{ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + /* Enable peripherals */ + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); + + /* Setup GPIO ports */ + gpio_set_mode(TMS_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN); + gpio_set_mode(TCK_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, TCK_PIN); + gpio_set_mode(TDI_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, TDI_PIN); + + gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, LED_IDLE_RUN); + + /* Setup heartbeat timer */ + systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8); + systick_set_reload(900000); /* Interrupt us at 10 Hz */ + systick_interrupt_enable(); + systick_counter_enable(); + + cdcacm_init(); + + jtag_scan(); + + return 0; +} + +void sys_tick_handler(void) +{ + if(running_status) + gpio_toggle(LED_PORT, LED_IDLE_RUN); + + if(timeout_counter) + timeout_counter--; +} + +const char *morse_msg; + +void morse(const char *msg, char repeat) +{ + (void)repeat; + morse_msg = msg; +} + +const char *platform_target_voltage(void) +{ + return "unknown"; +} diff --git a/src/platforms/stlink/platform.h b/src/platforms/stlink/platform.h new file mode 100644 index 0000000..bef7cde --- /dev/null +++ b/src/platforms/stlink/platform.h @@ -0,0 +1,142 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 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 the platform specific functions for the STM32 + * implementation. + */ +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#include + +#include +#include + +#include "gdb_packet.h" + +#define INLINE_GPIO +#define CDCACM_PACKET_SIZE 64 +#define CDCACM_GDB_ENDPOINT 1 + +/* Important pin mappings for STM32 implementation: + * + * LED0 = PB2 (Yellow LED : Running) + * LED1 = PB10 (Yellow LED : Idle) + * LED2 = PB11 (Red LED : Error) + * + * TPWR = RB0 (input) -- analogue on mini design ADC1, ch8 + * nTRST = PB1 + * SRST_OUT = PA2 + * TDI = PA3 + * TMS = PA4 (input for SWDP) + * TCK = PA5 + * TDO = PA6 (input) + * nSRST = PA7 (input) + * + * USB cable pull-up: PA8 + * USB VBUS detect: PB13 -- New on mini design. + * Enable pull up for compatibility. + * Force DFU mode button: PB12 + */ + +/* Hardware definitions... */ +#define TDI_PORT GPIOA +#define TMS_PORT GPIOB +#define TCK_PORT GPIOA +#define TDO_PORT GPIOA +#define TDI_PIN GPIO7 +#define TMS_PIN GPIO14 +#define TCK_PIN GPIO5 +#define TDO_PIN GPIO6 + +#define SWDIO_PORT TMS_PORT +#define SWCLK_PORT TCK_PORT +#define SWDIO_PIN TMS_PIN +#define SWCLK_PIN TCK_PIN + +#define LED_PORT GPIOA +#define LED_IDLE_RUN GPIO8 + +#define DEBUG(...) + +extern uint8_t running_status; +extern volatile uint32_t timeout_counter; + +extern jmp_buf fatal_error_jmpbuf; + +extern const char *morse_msg; + +#define gpio_set_val(port, pin, val) do { \ + if(val) \ + gpio_set((port), (pin)); \ + else \ + gpio_clear((port), (pin)); \ +} while(0) + +#define SET_RUN_STATE(state) {running_status = (state);} +#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);} + +#define PLATFORM_SET_FATAL_ERROR_RECOVERY() {setjmp(fatal_error_jmpbuf);} +#define PLATFORM_FATAL_ERROR(error) { \ + if(running_status) gdb_putpacketz("X1D"); \ + else gdb_putpacketz("EFF"); \ + running_status = 0; \ + TARGET_LIST_FREE(); \ + cur_target = last_target = NULL; \ + longjmp(fatal_error_jmpbuf, (error)); \ +} + +int platform_init(void); +void morse(const char *msg, char repeat); +const char *platform_target_voltage(void); + +/* */ +void cdcacm_init(void); +/* Returns current usb configuration, or 0 if not configured. */ +int cdcacm_get_config(void); +int cdcacm_get_dtr(void); + +/* Use newlib provided integer only stdio functions */ +#define sscanf siscanf +#define sprintf siprintf +#define vasprintf vasiprintf + +#ifdef INLINE_GPIO +static inline void _gpio_set(u32 gpioport, u16 gpios) +{ + GPIO_BSRR(gpioport) = gpios; +} +#define gpio_set _gpio_set + +static inline void _gpio_clear(u32 gpioport, u16 gpios) +{ + GPIO_BRR(gpioport) = gpios; +} +#define gpio_clear _gpio_clear + +static inline u16 _gpio_get(u32 gpioport, u16 gpios) +{ + return (u16)GPIO_IDR(gpioport) & gpios; +} +#define gpio_get _gpio_get +#endif + +#endif + diff --git a/src/platforms/native/blackmagic.ld b/src/platforms/stm32/blackmagic.ld similarity index 100% rename from src/platforms/native/blackmagic.ld rename to src/platforms/stm32/blackmagic.ld diff --git a/src/platforms/stm32/jtagtap.c b/src/platforms/stm32/jtagtap.c index 8160be8..a98c1d2 100644 --- a/src/platforms/stm32/jtagtap.c +++ b/src/platforms/stm32/jtagtap.c @@ -41,19 +41,23 @@ int jtagtap_init(void) void jtagtap_reset(void) { +#ifdef TRST_PORT volatile int i; gpio_clear(TRST_PORT, TRST_PIN); for(i = 0; i < 10000; i++) asm("nop"); gpio_set(TRST_PORT, TRST_PIN); +#endif jtagtap_soft_reset(); } void jtagtap_srst(void) { +#ifdef SRST_PORT volatile int i; gpio_set(SRST_PORT, SRST_PIN); for(i = 0; i < 10000; i++) asm("nop"); gpio_clear(SRST_PORT, SRST_PIN); +#endif } inline uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDO)