]> mj.ucw.cz Git - home-hw.git/commitdiff
BSB: Experiments with boot-loader
authorMartin Mares <mj@ucw.cz>
Mon, 24 Feb 2020 14:17:16 +0000 (15:17 +0100)
committerMartin Mares <mj@ucw.cz>
Mon, 24 Feb 2020 14:17:16 +0000 (15:17 +0100)
bsb/bootloader/Makefile [new file with mode: 0644]
bsb/bootloader/config.h [new file with mode: 0644]
bsb/bootloader/main.c [new file with mode: 0644]

diff --git a/bsb/bootloader/Makefile b/bsb/bootloader/Makefile
new file mode 100644 (file)
index 0000000..4c9abf3
--- /dev/null
@@ -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 (file)
index 0000000..dcb9ac7
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ *     BSB Interface Bootloader -- Configuration
+ *
+ *     (c) 2020 Martin Mareš <mj@ucw.cz>
+ */
+
+// 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 (file)
index 0000000..5071122
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ *     Bootloader for the BSB Interface
+ *
+ *     (c) 2020 Martin Mareš <mj@ucw.cz>
+ *
+ *     Based on example code from the libopencm3 project, which is
+ *     Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ *     Licensed under the GNU LGPL v3 or any later version.
+ */
+
+#include "util.h"
+
+#include <libopencm3/cm3/cortex.h>
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/scb.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/flash.h>
+#include <libopencm3/stm32/usart.h>
+#include <libopencm3/stm32/desig.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/dfu.h>
+
+#include <string.h>
+
+#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)();
+}