From 74768515dde1fcff02af514d2b86bf2464cf8e8d Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 24 Feb 2020 15:17:16 +0100 Subject: [PATCH] BSB: Experiments with boot-loader --- bsb/bootloader/Makefile | 6 + bsb/bootloader/config.h | 14 ++ bsb/bootloader/main.c | 324 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 bsb/bootloader/Makefile create mode 100644 bsb/bootloader/config.h create mode 100644 bsb/bootloader/main.c diff --git a/bsb/bootloader/Makefile b/bsb/bootloader/Makefile new file mode 100644 index 0000000..4c9abf3 --- /dev/null +++ b/bsb/bootloader/Makefile @@ -0,0 +1,6 @@ +ROOT=../.. +BINARY=bootloader +OBJS=main.o +LIB_OBJS=util-debug.o + +include $(ROOT)/mk/bluepill.mk diff --git a/bsb/bootloader/config.h b/bsb/bootloader/config.h new file mode 100644 index 0000000..dcb9ac7 --- /dev/null +++ b/bsb/bootloader/config.h @@ -0,0 +1,14 @@ +/* + * BSB Interface Bootloader -- Configuration + * + * (c) 2020 Martin Mareš + */ + +// Processor clock + +#define CPU_CLOCK_MHZ 48 + +// Debugging port + +#define DEBUG_USART USART1 +#define DEBUG_LED_BLUEPILL diff --git a/bsb/bootloader/main.c b/bsb/bootloader/main.c new file mode 100644 index 0000000..5071122 --- /dev/null +++ b/bsb/bootloader/main.c @@ -0,0 +1,324 @@ +/* + * Bootloader for the BSB Interface + * + * (c) 2020 Martin Mareš + * + * Based on example code from the libopencm3 project, which is + * Copyright (C) 2010 Gareth McMullin + * + * Licensed under the GNU LGPL v3 or any later version. + */ + +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEBUG_BOOTLOADER + +#ifdef DEBUG_BOOTLOADER +#define DEBUG(x...) debug_printf(x) +#else +#define DEBUG(x...) do { } while (0) +#endif + +#define APP_ADDRESS 0x08002000 + +byte usbd_control_buffer[1024]; + +static enum dfu_state usbdfu_state = STATE_DFU_IDLE; + +static struct { + byte buf[sizeof(usbd_control_buffer)]; + u16 len; + u32 addr; + u16 blocknum; +} prog; + +static char usb_serial_number[13]; + +enum usb_string { + STR_MANUFACTURER = 1, + STR_DEVICE, + STR_SERIAL, +}; + +static const char *usb_strings[] = { + "United Computer Wizards", + "Boot Loader", + usb_serial_number, +}; + +const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x4242, + .idProduct = 0xdead, + .bcdDevice = 0x0100, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_DEVICE, + .iSerialNumber = STR_SERIAL, + .bNumConfigurations = 1, +}; + +const struct usb_dfu_descriptor dfu_function = { + .bLength = sizeof(struct usb_dfu_descriptor), + .bDescriptorType = DFU_FUNCTIONAL, + .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, + .wDetachTimeout = 255, + .wTransferSize = 1024, + .bcdDFUVersion = 0x0100, +}; + +const struct usb_interface_descriptor iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */ + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 2, + .extra = &dfu_function, + .extralen = sizeof(dfu_function), +}; + +const struct usb_interface ifaces[] = {{ + .num_altsetting = 1, + .altsetting = &iface, +}}; + +const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATTR_DEFAULT, // bus-powered + .bMaxPower = 50, // multiplied by 2 mA + .interface = ifaces, +}; + +static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout) +{ + switch (usbdfu_state) { + case STATE_DFU_DNLOAD_SYNC: + usbdfu_state = STATE_DFU_DNBUSY; + *bwPollTimeout = 100; + return DFU_STATUS_OK; + case STATE_DFU_MANIFEST_SYNC: + /* Device will reset when read is complete. */ + usbdfu_state = STATE_DFU_MANIFEST; + return DFU_STATUS_OK; + default: + return DFU_STATUS_OK; + } +} + +static void usbdfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setup_data *req UNUSED) +{ + switch (usbdfu_state) { + case STATE_DFU_DNBUSY: { + flash_unlock(); + u32 baseaddr = APP_ADDRESS + prog.blocknum * dfu_function.wTransferSize; + DEBUG("DFU: Block %u -> %08x\n", prog.blocknum, (uint) baseaddr); + flash_erase_page(baseaddr); + for (uint i = 0; i < prog.len; i += 2) { + u16 *dat = (u16 *)(prog.buf + i); + flash_program_half_word(baseaddr + i, *dat); + } + flash_lock(); + usbdfu_state = STATE_DFU_DNLOAD_IDLE; + return; + } + case STATE_DFU_MANIFEST: + /* USB device must detach, we just reset... */ + scb_reset_system(); + return; /* Will never return. */ + default: + return; + } +} + +static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_dev, + struct usb_setup_data *req, + byte **buf, + u16 *len, + void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) +{ + if ((req->bmRequestType & 0x7F) != 0x21) + return USBD_REQ_NOTSUPP; /* Only accept class request. */ + + DEBUG("DFU: req %02x in state %d\n", req->bRequest, usbdfu_state); + + switch (req->bRequest) { + case DFU_DNLOAD: + if (len == NULL || *len == 0) { + usbdfu_state = STATE_DFU_MANIFEST_SYNC; + } else { + /* Copy download data for use on GET_STATUS. */ + prog.blocknum = req->wValue; + prog.len = *len; + memcpy(prog.buf, *buf, *len); + usbdfu_state = STATE_DFU_DNLOAD_SYNC; + } + return USBD_REQ_HANDLED; + case DFU_CLRSTATUS: + /* Clear error and return to dfuIDLE. */ + if (usbdfu_state == STATE_DFU_ERROR) + usbdfu_state = STATE_DFU_IDLE; + return USBD_REQ_HANDLED; + case DFU_ABORT: + /* Abort returns to dfuIDLE state. */ + usbdfu_state = STATE_DFU_IDLE; + return USBD_REQ_HANDLED; + case DFU_UPLOAD: + /* Upload not supported for now. */ + return USBD_REQ_NOTSUPP; + case DFU_GETSTATUS: { + u32 bwPollTimeout = 0; /* 24-bit number of milliseconds */ + (*buf)[0] = usbdfu_getstatus(usbd_dev, &bwPollTimeout); + (*buf)[1] = bwPollTimeout & 0xFF; + (*buf)[2] = (bwPollTimeout >> 8) & 0xFF; + (*buf)[3] = (bwPollTimeout >> 16) & 0xFF; + (*buf)[4] = usbdfu_state; + (*buf)[5] = 0; /* iString not used here */ + *len = 6; + *complete = usbdfu_getstatus_complete; + return USBD_REQ_HANDLED; + } + case DFU_GETSTATE: + /* Return state with no state transition. */ + *buf[0] = usbdfu_state; + *len = 1; + return USBD_REQ_HANDLED; + } + + return USBD_REQ_NOTSUPP; +} + +static void usbdfu_set_config(usbd_device *usbd_dev, u16 wValue UNUSED) +{ + usbd_register_control_callback( + usbd_dev, + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + usbdfu_control_request); +} + +static void usbdfu_reset(void) +{ + usbdfu_state = STATE_DFU_IDLE; +} + +int main(void) +{ + usbd_device *usbd_dev; + + // Flash programming requires running on the internal oscillator + rcc_clock_setup_in_hsi_out_48mhz(); + + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_USB); + + rcc_periph_reset_pulse(RST_GPIOA); + rcc_periph_reset_pulse(RST_GPIOC); + rcc_periph_reset_pulse(RST_USB); + +#ifdef DEBUG_BOOTLOADER + rcc_periph_clock_enable(RCC_USART1); + rcc_periph_reset_pulse(RST_USART1); + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9); + + usart_set_baudrate(USART1, 115200); + usart_set_databits(USART1, 8); + usart_set_stopbits(USART1, USART_STOPBITS_1); + usart_set_mode(USART1, USART_MODE_TX); + usart_set_parity(USART1, USART_PARITY_NONE); + usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + + usart_enable(USART1); +#endif + + // BluePill LED + gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); + debug_led(1); + + // Systick: set to overflow in 1 ms, will use only the overflow flag, no interrupts + systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000); + systick_clear(); + systick_counter_enable(); + + desig_get_unique_id_as_dfu(usb_serial_number); + + DEBUG("Entered boot-loader\n"); + + // Simulate USB disconnect + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12); + gpio_clear(GPIOA, GPIO11 | GPIO12); + for (uint i=0; i<1000; i++) { + while (!systick_get_countflag()) + ; + } + + DEBUG("DFU: Ready\n"); + debug_led(0); + + usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer)); + usbd_register_reset_callback(usbd_dev, usbdfu_reset); + usbd_register_set_config_callback(usbd_dev, usbdfu_set_config); + +restart: ; + + uint timeout = 5000; + while (timeout) { + usbd_poll(usbd_dev); + if (systick_get_countflag()) { + timeout--; + if (!(timeout & 0x3f)) + debug_led_toggle(); + } + } + + volatile u32 *app = (volatile u32 *) APP_ADDRESS; + u32 sp = app[0]; + u32 pc = app[1]; + + DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc); + if (sp & 0x2ffe0000 != 0x20000000) { + DEBUG("DFU: Suspicious SP, refusing to start\n"); + goto restart; + } + + while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC)) + ; + debug_led(0); + cm_disable_interrupts(); + + /* Set vector table base address. */ + SCB_VTOR = APP_ADDRESS & 0xFFFF; + + /* Initialize master stack pointer. */ + asm volatile("msr msp, %0"::"g" (sp)); + + /* Jump to application. */ + ((void (*)(void)) pc)(); +} -- 2.39.2