From 9eca23adc9b1eb7393af268214bfcf3f9cae58c0 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sat, 26 Aug 2017 22:12:15 -0700 Subject: [PATCH 01/66] Update erase target to handle unlocking flash --- src/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index e1b10dd..bb68e9b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -44,7 +44,10 @@ debug: $(BINARY).elf -$(GDB) --tui --eval "target remote | $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) -f debug.cfg" $(BINARY).elf erase: - $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) -c "init" -c "reset init" -c "flash erase_sector 0 0 last" -c "reset" -c "shutdown" + $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) \ + -c "init" -c "reset init" \ + -c "stm32f1x unlock 0; reset halt" \ + -c "flash erase_sector 0 0 last" -c "reset" -c "shutdown" .PHONY += debug size erase From e6147fa55d55b0f018455573aa00bdf4f5d5bb69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20B=C3=ADlek?= Date: Sun, 3 Dec 2017 03:49:08 +0100 Subject: [PATCH 02/66] Using WINUSB_MS_VENDOR_CODE in extra string This might make some future debugging easier. --- src/winusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/winusb.c b/src/winusb.c index 38bf644..f009500 100644 --- a/src/winusb.c +++ b/src/winusb.c @@ -78,7 +78,8 @@ static void winusb_set_config(usbd_device* usbd_dev, uint16_t wValue) { } void winusb_setup(usbd_device* usbd_dev) { - usbd_register_extra_string(usbd_dev, 0xEE, "MSFT100!"); + static const char msft_extra_string[] = {'M', 'S', 'F', 'T', '1', '0', '0', WINUSB_MS_VENDOR_CODE, '\0'}; + usbd_register_extra_string(usbd_dev, 0xEE, msft_extra_string); usbd_register_set_config_callback(usbd_dev, winusb_set_config); /* Windows probes the compatible ID before setting the configuration, From ebc9b4bc73b47d52c58eeb35b3e9c0fd7868d147 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Tue, 27 Feb 2018 22:29:09 -0800 Subject: [PATCH 03/66] Migrate from CircleCI 1.0 to 2.0 Update circle.yml to the new format .circleci/config.yml Update install-toolchain.sh to the more robust version from dap42 --- .circleci/config.yml | 25 +++++++++++++++++++++++++ circle.yml | 12 ------------ util/install-toolchain.sh | 32 ++++++++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 circle.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..01c3d83 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,25 @@ +version: 2 +jobs: + build: + machine: true + steps: + - checkout + - run: + name: Checkout submodules + command: git submodule sync && git submodule update --init + - restore_cache: + key: v1-toolchain-checksum-{{ checksum "util/install-toolchain.sh" }} + - run: + name: Install toolchain + command: ./util/install-toolchain.sh + - save_cache: + key: v1-toolchain-checksum-{{ checksum "util/install-toolchain.sh" }} + paths: + - "~/toolchains/" + - run: + name: Compile firmware + command: make -f release.Makefile -k all + environment: + PREFIX: "~/toolchains/gcc-arm-embedded/bin/arm-none-eabi" + - store_artifacts: + path: build/ diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 279d8f6..0000000 --- a/circle.yml +++ /dev/null @@ -1,12 +0,0 @@ -dependencies: - override: - - ./util/install-toolchain.sh - cache_directories: - - "~/toolchains/" -test: - override: - - PREFIX="~/toolchains/gcc-arm-embedded/bin/arm-none-eabi" make -f release.Makefile -k all - -general: - artifacts: - - build/ \ No newline at end of file diff --git a/util/install-toolchain.sh b/util/install-toolchain.sh index 1248518..f25f950 100755 --- a/util/install-toolchain.sh +++ b/util/install-toolchain.sh @@ -1,8 +1,32 @@ #!/bin/bash +set -eo pipefail URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 TOOLCHAIN=gcc-arm-none-eabi-6-2017-q2-update -if [[ ! -d "$HOME/toolchains/gcc-arm-embedded" ]]; then - mkdir -p ~/toolchains - wget -qO- $URL | tar xj -C ~/toolchains/ - ln -s $TOOLCHAIN ~/toolchains/gcc-arm-embedded +TOOLCHAINS=$HOME/toolchains +TOOLCHAIN_MISSING=0 +GCC=${TOOLCHAINS}/gcc-arm-embedded/bin/arm-none-eabi-gcc +if [[ ! -d "${TOOLCHAINS}/gcc-arm-embedded" ]]; then + TOOLCHAIN_MISSING=1 fi; +if [[ ! -f ${GCC} ]]; then + TOOLCHAIN_MISSING=1 +fi; + +if [ $TOOLCHAIN_MISSING -eq 1 ]; then + echo "Installing $TOOLCHAIN from $URL to ${TOOLCHAINS}" + mkdir -p ${TOOLCHAINS} + wget -qO- $URL | tar xj -C ${TOOLCHAINS} + rm -rf ${TOOLCHAINS}/gcc-arm-embedded + ln -s $TOOLCHAIN ${TOOLCHAINS}/gcc-arm-embedded +fi; + +EXISTING_TOOLCHAIN=`readlink -f "${TOOLCHAINS}/gcc-arm-embedded"` +echo "Current toolchain is $EXISTING_TOOLCHAIN" + +if ! ldd ${GCC} >/dev/null; then + echo "${GCC} does not appear to be executable on this machine" + exit 1 +fi; + +TOOLCHAIN_VER=`${GCC} --version | head -n 1` +echo "Installed toolchain version is $TOOLCHAIN_VER" From 9b16a3631dd976c4e8de103d8c421ccc1d6b5f02 Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 09:11:20 -0300 Subject: [PATCH 04/66] Fixes issue #13. Added a delay(Approximately 20ms) before reading the button. --- src/stm32f103/target_stm32f103.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 10b07d9..69c21db 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -161,6 +161,10 @@ bool target_get_force_bootloader(void) { backup_write(BKP0, 0); #if HAVE_BUTTON + /* Wait sometime in case the button has some debounce capacitor */ + for (int i = 0; i < 1440000; i++) { + __asm__("nop"); + } /* Check if the user button is held down */ if (BUTTON_ACTIVE_HIGH) { if (gpio_get(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN)) { From 721650827b90c7d3a7a601a94f31134374b3ff96 Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 13:22:38 -0300 Subject: [PATCH 05/66] Added a variable to control the button sample delay. --- src/stm32f103/generic/config.h | 4 ++++ src/stm32f103/target_stm32f103.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/stm32f103/generic/config.h b/src/stm32f103/generic/config.h index 9588a6f..960d852 100644 --- a/src/stm32f103/generic/config.h +++ b/src/stm32f103/generic/config.h @@ -33,6 +33,10 @@ #define HAVE_BUTTON 0 #endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + #ifndef HAVE_USB_PULLUP_CONTROL #define HAVE_USB_PULLUP_CONTROL 0 #endif diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 69c21db..41f2382 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -162,7 +162,7 @@ bool target_get_force_bootloader(void) { #if HAVE_BUTTON /* Wait sometime in case the button has some debounce capacitor */ - for (int i = 0; i < 1440000; i++) { + for (int i = 0; i < BUTTON_SAMPLE_DELAY_CYCLES; i++) { __asm__("nop"); } /* Check if the user button is held down */ From a1975ed1f21a6036b61e649b1efb87171796e32d Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 13:42:17 -0300 Subject: [PATCH 06/66] Fix issue #15. --- src/dfu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dfu.c b/src/dfu.c index 7c2a107..7351eb6 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -231,6 +231,7 @@ static int dfu_control_class_request(usbd_device *usbd_dev, case STATE_DFU_IDLE: { current_dfu_offset = 0; /* Fall through */ + __attribute__ ((fallthrough)); } case STATE_DFU_UPLOAD_IDLE: { *buf = (uint8_t*)(APP_BASE_ADDRESS + current_dfu_offset); From b1f4a08edb959b0eef6c8084ce76971a90e33634 Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 13:49:49 -0300 Subject: [PATCH 07/66] Added a README entry explaining the button usage. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f860799..b0b61c6 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ The bootloader can be built to look for arbitrary patterns, but the default for The bootloader currently looks for `0x544F` in RTC backup register 1 and `0x4F42` in RTC backup register 0 (together they spell "BOOT" in ASCII). +You can also use a button to stay in bootloader while booting. It's configured using `HAVE_BUTTON` define. If your button has a debounce capacitor, you can use `BUTTON_SAMPLE_DELAY_CYCLES` define to specify how many cycles to wait before sampling the I/O pin, by default it is approximately 20ms in a 72Mhz MCU. + ### WebUSB This bootloader implements the draft [WebUSB](https://wicg.github.io/webusb/) specification, which allows web pages to access the bootloader (after presenting the user with a device picker dialog). From e3a77a80f1b63f6e6faef53f6f591834396bad4b Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 14:09:58 -0300 Subject: [PATCH 08/66] Fix C89 compatibility. --- src/stm32f103/target_stm32f103.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 41f2382..281f1cb 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -162,7 +162,8 @@ bool target_get_force_bootloader(void) { #if HAVE_BUTTON /* Wait sometime in case the button has some debounce capacitor */ - for (int i = 0; i < BUTTON_SAMPLE_DELAY_CYCLES; i++) { + int i; + for (i = 0; i < BUTTON_SAMPLE_DELAY_CYCLES; i++) { __asm__("nop"); } /* Check if the user button is held down */ From d78d0f72e32620c31208e763f71547292e215cb0 Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 14:25:07 -0300 Subject: [PATCH 09/66] Fix a miserable typo. --- src/stm32f103/target_stm32f103.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 281f1cb..d03f2c2 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -161,7 +161,7 @@ bool target_get_force_bootloader(void) { backup_write(BKP0, 0); #if HAVE_BUTTON - /* Wait sometime in case the button has some debounce capacitor */ + /* Wait some time in case the button has some debounce capacitor */ int i; for (i = 0; i < BUTTON_SAMPLE_DELAY_CYCLES; i++) { __asm__("nop"); From 8bb813a292e059d2d459619fb81429c37c40425e Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 16:24:15 -0300 Subject: [PATCH 10/66] Fixes issue #17. Added guard on all defines in config.h, allowing them to be overridden by make. --- README.md | 8 ++++++++ src/stm32f103/generic/config.h | 28 ++++++++++++++++++++++++++ src/stm32f103/maplemini/config.h | 34 ++++++++++++++++++++++++++++++++ src/stm32f103/stlink/config.h | 24 ++++++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/README.md b/README.md index b0b61c6..22ab7bf 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,14 @@ Here is an example `local.mk` that changes the default target to the STLink/v2 a TARGET ?= STLINK OOCD_INTERFACE ?= interface/stlink-v2.cfg +You can also use the env variable `DEFS` to override default configuration for a target, like: + + # Disable LED on BluePill + DEFS="-DHAVE_LED=0" make TARGET=BLUEPILL + + # Allow access to all Flash on MapleMini and change the app base address + DEFS="-DFLASH_SIZE_OVERRIDE=0x20000 -DAPP_BASE_ADDRESS=0x08004000" make TARGET=MAPLEMINI LDSCRIPT="/some/folder/stm32f103x8-16kb-boot.ld" + ## Using the bootloader ### Building for the bootloader The bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate. diff --git a/src/stm32f103/generic/config.h b/src/stm32f103/generic/config.h index 960d852..8bfe845 100644 --- a/src/stm32f103/generic/config.h +++ b/src/stm32f103/generic/config.h @@ -19,19 +19,47 @@ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED +#ifndef APP_BASE_ADDRESS #define APP_BASE_ADDRESS 0x08002000 +#endif +#ifndef FLASH_SIZE_OVERRIDE #define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE #define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE #define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 +#endif #ifndef HAVE_LED #define HAVE_LED 0 #endif +// #ifndef LED_OPEN_DRAIN +// #define LED_OPEN_DRAIN 0 +// #endif +// #ifndef LED_GPIO_PORT +// #define LED_GPIO_PORT GPIOA +// #endif +// #ifndef LED_GPIO_PIN +// #define LED_GPIO_PIN GPIO0 +// #endif #ifndef HAVE_BUTTON #define HAVE_BUTTON 0 #endif +// #ifndef BUTTON_ACTIVE_HIGH +// #define BUTTON_ACTIVE_HIGH 0 +// #endif +// #ifndef BUTTON_GPIO_PORT +// #define BUTTON_GPIO_PORT GPIOA +// #endif +// #ifndef BUTTON_GPIO_PIN +// #define BUTTON_GPIO_PIN GPIO0 +// #endif #ifndef BUTTON_SAMPLE_DELAY_CYCLES #define BUTTON_SAMPLE_DELAY_CYCLES 1440000 diff --git a/src/stm32f103/maplemini/config.h b/src/stm32f103/maplemini/config.h index 1fae49f..f44ebc2 100644 --- a/src/stm32f103/maplemini/config.h +++ b/src/stm32f103/maplemini/config.h @@ -19,31 +19,65 @@ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED +#ifndef APP_BASE_ADDRESS #define APP_BASE_ADDRESS 0x08002000 +#endif +#ifndef FLASH_PAGE_SIZE #define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE #define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 +#endif +#ifndef HAVE_LED #define HAVE_LED 1 +#endif +#ifndef LED_GPIO_PORT #define LED_GPIO_PORT GPIOB +#endif +#ifndef LED_GPIO_PIN #define LED_GPIO_PIN GPIO1 +#endif +#ifndef LED_OPEN_DRAIN #define LED_OPEN_DRAIN 0 +#endif /* Technically, there is a button on PB8, but the button is also shorted to BOOT0, so it's not very useful for us to sample PB8 on boot, since pulling it high will already trigger the ROM serial bootloader and prevent us from running anyways. */ +#ifndef HAVE_BUTTON #define HAVE_BUTTON 0 +#endif +#ifndef HAVE_USB_PULLUP_CONTROL #define HAVE_USB_PULLUP_CONTROL 1 +#endif +#ifndef USB_PULLUP_GPIO_PORT #define USB_PULLUP_GPIO_PORT GPIOB +#endif +#ifndef USB_PULLUP_GPIO_PIN #define USB_PULLUP_GPIO_PIN GPIO9 +#endif +#ifndef USB_PULLUP_ACTIVE_HIGH #define USB_PULLUP_ACTIVE_HIGH 0 +#endif +#ifndef USB_PULLUP_OPEN_DRAIN #define USB_PULLUP_OPEN_DRAIN 1 +#endif +#ifndef USES_GPIOA #define USES_GPIOA 0 +#endif +#ifndef USES_GPIOB #define USES_GPIOB 1 +#endif +#ifndef USES_GPIOC #define USES_GPIOC 0 +#endif #endif diff --git a/src/stm32f103/stlink/config.h b/src/stm32f103/stlink/config.h index c55ef5b..b0127b4 100644 --- a/src/stm32f103/stlink/config.h +++ b/src/stm32f103/stlink/config.h @@ -19,21 +19,45 @@ #ifndef CONFIG_H_INCLUDED #define CONFIG_H_INCLUDED +#ifndef APP_BASE_ADDRESS #define APP_BASE_ADDRESS 0x08002000 +#endif +#ifndef FLASH_SIZE_OVERRIDE #define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE #define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE #define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 +#endif +#ifndef HAVE_LED #define HAVE_LED 1 +#endif +#ifndef LED_GPIO_PORT #define LED_GPIO_PORT GPIOA +#endif +#ifndef LED_GPIO_PIN #define LED_GPIO_PIN GPIO9 +#endif +#ifndef LED_OPEN_DRAIN #define LED_OPEN_DRAIN 0 +#endif +#ifndef HAVE_BUTTON #define HAVE_BUTTON 0 +#endif +#ifndef HAVE_USB_PULLUP_CONTROL #define HAVE_USB_PULLUP_CONTROL 0 +#endif +#ifndef USES_GPIOA #define USES_GPIOA 1 +#endif #endif From fed09dd16cc7b81e906e0cf5939295eca987b0cc Mon Sep 17 00:00:00 2001 From: Tercio Gaudencio Filho Date: Fri, 9 Aug 2019 16:25:28 -0300 Subject: [PATCH 11/66] Created a target for BluePill, allowing it to be overridden. --- src/stm32f103/bluepill/config.h | 67 +++++++++++++++++++++++++++++++++ src/targets.mk | 3 +- 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/stm32f103/bluepill/config.h diff --git a/src/stm32f103/bluepill/config.h b/src/stm32f103/bluepill/config.h new file mode 100644 index 0000000..28677e2 --- /dev/null +++ b/src/stm32f103/bluepill/config.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS 0x08002000 +#endif +#ifndef FLASH_SIZE_OVERRIDE +#define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 1 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOC +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO13 +#endif + +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 0 +#endif + +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 0 +#endif + +#ifndef USES_GPIOC +#define USES_GPIOC 1 +#endif + +#endif diff --git a/src/targets.mk b/src/targets.mk index 0b9359c..d93a11a 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -22,10 +22,9 @@ ifeq ($(TARGET),STM32F103) endif ifeq ($(TARGET),BLUEPILL) TARGET_COMMON_DIR := ./stm32f103 - TARGET_SPEC_DIR := ./stm32f103/generic + TARGET_SPEC_DIR := ./stm32f103/bluepill LDSCRIPT := ./stm32f103/stm32f103x8.ld ARCH = STM32F1 - DEFS += -DHAVE_LED=1 -DLED_GPIO_PORT=GPIOC -DLED_GPIO_PIN=GPIO13 -DLED_OPEN_DRAIN=1 -DUSES_GPIOC=1 endif ifeq ($(TARGET),MAPLEMINI) TARGET_COMMON_DIR := ./stm32f103 From a9e1f573dd5ad86a25603b79b60ce7d6e639e633 Mon Sep 17 00:00:00 2001 From: Kovacsics Robert Date: Wed, 26 Feb 2020 21:18:25 +0000 Subject: [PATCH 12/66] Don't compile all of libopencm3, only required target --- src/rules.mk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rules.mk b/src/rules.mk index b139e4f..331c3cc 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -34,6 +34,7 @@ ifeq ($(ARCH),STM32F0) FP_FLAGS ?= -msoft-float ARCH_FLAGS = -mthumb -mcpu=cortex-m0 $(FP_FLAGS) OOCD_BOARD ?= target/stm32f0x.cfg + OPENCM3_TARGET = "stm32/f0" endif ifeq ($(ARCH),STM32F1) LIBNAME = opencm3_stm32f1 @@ -41,12 +42,14 @@ ifeq ($(ARCH),STM32F1) FP_FLAGS ?= -msoft-float ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd OOCD_BOARD ?= target/stm32f1x.cfg + OPENCM3_TARGET = "stm32/f1" endif LIBNAME ?= opencm3_stm32f0 DEFS ?= -DSTM32F0 FP_FLAGS ?= -msoft-float ARCH_FLAGS ?= -mthumb -mcpu=cortex-m0 $(FP_FLAGS) +OPENCM3_TARGET ?= "stm32/f0" #################################################################### # Semihosting support @@ -156,7 +159,7 @@ $(OPENCM3_DIR)/Makefile: $(Q)git submodule update --init $(OPENCM3_DIR) $(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/Makefile - $(Q)$(MAKE) -C $(OPENCM3_DIR) + $(Q)$(MAKE) -C $(OPENCM3_DIR) TARGETS=$(OPENCM3_TARGET) locm3: $(LIB_DIR)/lib$(LIBNAME).a From 941538351c37cdb522662df24d0928171dcca3d6 Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Thu, 16 Apr 2020 23:43:24 -0400 Subject: [PATCH 13/66] Allow configuring button pull-ups in each target. --- src/stm32f103/target_stm32f103.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index d03f2c2..7c25942 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -45,6 +45,10 @@ #define USES_GPIOC 0 #endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif + #ifdef FLASH_SIZE_OVERRIDE _Static_assert((FLASH_BASE + FLASH_SIZE_OVERRIDE >= APP_BASE_ADDRESS), "Incompatible flash size"); @@ -95,12 +99,15 @@ void target_gpio_setup(void) { #if HAVE_BUTTON { const uint8_t mode = GPIO_MODE_INPUT; - const uint8_t conf = GPIO_CNF_INPUT_PULL_UPDOWN; + const uint8_t conf = (BUTTON_USES_PULL ? GPIO_CNF_INPUT_PULL_UPDOWN + : GPIO_CNF_INPUT_FLOAT); gpio_set_mode(BUTTON_GPIO_PORT, mode, conf, BUTTON_GPIO_PIN); - if (BUTTON_ACTIVE_HIGH) { - gpio_clear(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); - } else { - gpio_set(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); + if (BUTTON_USES_PULL) { + if (BUTTON_ACTIVE_HIGH) { + gpio_clear(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); + } else { + gpio_set(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); + } } } #endif From 7595c1520ccd7942b26b3a34665e08ef6e3db4bb Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Thu, 16 Apr 2020 23:44:57 -0400 Subject: [PATCH 14/66] bluepill: Use the BOOT1 input in to enter the bootloader. The bluepill board has a dip-switch connected to BOOT1 (PB2) via a 100k resistor. Enter the bootloader when connecting this to "1". --- src/stm32f103/bluepill/config.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/stm32f103/bluepill/config.h b/src/stm32f103/bluepill/config.h index 28677e2..c0df9e0 100644 --- a/src/stm32f103/bluepill/config.h +++ b/src/stm32f103/bluepill/config.h @@ -49,7 +49,20 @@ #endif #ifndef HAVE_BUTTON -#define HAVE_BUTTON 0 +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 1 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOB +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO2 +#endif +// Blue-Pull has 100k resistors on PB2, so we can't use weak pulls to read it. +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 0 #endif #ifndef BUTTON_SAMPLE_DELAY_CYCLES @@ -60,6 +73,10 @@ #define HAVE_USB_PULLUP_CONTROL 0 #endif +#ifndef USES_GPIOB +#define USES_GPIOB 1 +#endif + #ifndef USES_GPIOC #define USES_GPIOC 1 #endif From 7b62074db8cabd988713be513d8d2a0524c3e075 Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Fri, 17 Apr 2020 00:02:07 -0400 Subject: [PATCH 15/66] Use link-time-optimization in the build. Using gcc-arm-none-eabi-9-2019-q4-major this gives a bootloader binary about 900 bytes smaller. --- src/rules.mk | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rules.mk b/src/rules.mk index 331c3cc..30943f3 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -78,7 +78,7 @@ PREFIX ?= arm-none-eabi CC := $(PREFIX)-gcc CXX := $(PREFIX)-g++ LD := $(PREFIX)-gcc -AR := $(PREFIX)-ar +AR := $(PREFIX)-gcc-ar AS := $(PREFIX)-as OBJCOPY := $(PREFIX)-objcopy OBJDUMP := $(PREFIX)-objdump @@ -94,7 +94,7 @@ LIB_DIR = $(OPENCM3_DIR)/lib #################################################################### # C flags -CFLAGS += -Os -g -std=gnu11 +CFLAGS += -Os -flto -g -std=gnu11 CFLAGS += -Wextra -Wshadow -Wimplicit-function-declaration CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes CFLAGS += -fno-common -ffunction-sections -fdata-sections @@ -102,7 +102,7 @@ CFLAGS += -fno-common -ffunction-sections -fdata-sections #################################################################### # C++ flags -CXXFLAGS += -Os -g +CXXFLAGS += -Os -flto -g CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ CXXFLAGS += -fno-common -ffunction-sections -fdata-sections @@ -116,7 +116,7 @@ CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS) #################################################################### # Linker flags -LDFLAGS += --static -nostartfiles +LDFLAGS += -flto -Os -g --static -nostartfiles LDFLAGS += -L$(LIB_DIR) LDFLAGS += -T$(LDSCRIPT) LDFLAGS += -Wl,-Map=$(*).map @@ -159,7 +159,7 @@ $(OPENCM3_DIR)/Makefile: $(Q)git submodule update --init $(OPENCM3_DIR) $(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/Makefile - $(Q)$(MAKE) -C $(OPENCM3_DIR) TARGETS=$(OPENCM3_TARGET) + $(Q)$(MAKE) -C $(OPENCM3_DIR) AR=$(AR) CFLAGS="-flto -g" TARGETS=$(OPENCM3_TARGET) locm3: $(LIB_DIR)/lib$(LIBNAME).a From e0bde59770a49161b5135b0d8d7c3dca45cce53e Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Fri, 1 May 2020 23:01:26 -0400 Subject: [PATCH 16/66] Link with newlib-nano, this is 348 bytes smaller. --- src/rules.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rules.mk b/src/rules.mk index 30943f3..91ba674 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -117,6 +117,7 @@ CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS) # Linker flags LDFLAGS += -flto -Os -g --static -nostartfiles +LDFLAGS += -specs=nano.specs LDFLAGS += -L$(LIB_DIR) LDFLAGS += -T$(LDSCRIPT) LDFLAGS += -Wl,-Map=$(*).map From 0dfd64ed77da4cb631e528cb2cde9c5a5e4328e4 Mon Sep 17 00:00:00 2001 From: Daniel Serpell Date: Sat, 2 May 2020 00:27:42 -0400 Subject: [PATCH 17/66] Implement experimental high-memory bootloader Adds a compilation define `BOOTLOADER_HIGH` that compiles a high-memory version of the bootloader. The high-memory bootloader lives at the high end of flash, allowing an non- bootloader-aware application to run without modifications. The bootloader modifies the application's vector table to point to the bootloader's reset stub. The actual reset vector is stored in unused entries in the application's vector table. Based on ideas from: http://stm32duino.com/viewtopic.php?t=687 This is based on code from commit cf21dc3 fixing DFU upload and rebasing on current master branch. Co-authored-by: Devan Lai --- README.md | 21 +++++- release.Makefile | 44 +++++++++++- src/dapboot.c | 15 ++-- src/dapboot.h | 18 +++++ src/dfu.c | 37 +++++++++- src/stm32f103/bluepill/config.h | 2 +- src/stm32f103/generic/config.h | 2 +- src/stm32f103/maplemini/config.h | 2 +- src/stm32f103/stlink/config.h | 2 +- src/stm32f103/stm32f1.ld | 109 ++++++++++++++++++++++++++++++ src/stm32f103/stm32f103x8.ld | 10 +-- src/stm32f103/stm32f103x8_high.ld | 31 +++++++++ src/stm32f103/stm32f103xb_high.ld | 31 +++++++++ src/stm32f103/target_stm32f103.c | 1 + src/targets.mk | 42 ++++++++++++ 15 files changed, 349 insertions(+), 18 deletions(-) create mode 100644 src/stm32f103/stm32f1.ld create mode 100644 src/stm32f103/stm32f103x8_high.ld create mode 100644 src/stm32f103/stm32f103xb_high.ld diff --git a/README.md b/README.md index 22ab7bf..745dec6 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,20 @@ To build other targets, you can override the |`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | http://wiki.stm32duino.com/index.php?title=Maple_Mini | |`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones | +For each of the above targets, there are three variants that can be added to the target name: + +| Target Variant | Description | +| -------------- | ----------------------------------------------------- | +|` ` | Standard bootloader, using first 8kB of flash | +|`_HIGH` | High memory bootloader for 64kB chips (experimental) | +|`_HIGH_128` | High memory bootloader for 128kB chips (experimental) | + +The high memory bootloader is a variation that doesn't require the application to be at an offset, the bootloader resides in the top 6.5kB of ROM and hides its reset and stack vectors inside unused entries of the application vector table. As an example, to compile for a Bluepill board with 128kB flash, use: + + make clean + make TARGET=BLUEPILL_HIGH_128 + + ## Flash instructions The `make flash` target will use openocd to upload the bootloader to an attached board. By default, the Makefile assumes you're using a [CMSIS-DAP](http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php) based probe, but you can override this by overriding `OOCD_INTERFACE` variable. For example: @@ -43,9 +57,10 @@ You can also use the env variable `DEFS` to override default configuration for a ## Using the bootloader ### Building for the bootloader -The bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate. +The standard bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate. + +The high memory bootloaders do not use the lower part of the flash, so you only need to make sure your application leaves 6.5kB of flash free. -See the [highboot branch](https://github.com/devanlai/dapboot/tree/highboot) for an experimental variation that doesn't require the application to be offset. ### Switching to the bootloader The bootloader can be built to look for arbitrary patterns, but the default for the STM32F103 target looks for a magic value stored in the RTC backup registers. Writing the magic value and then resetting will run the bootloader instead of the main application. @@ -54,6 +69,8 @@ The bootloader currently looks for `0x544F` in RTC backup register 1 and `0x4F42 You can also use a button to stay in bootloader while booting. It's configured using `HAVE_BUTTON` define. If your button has a debounce capacitor, you can use `BUTTON_SAMPLE_DELAY_CYCLES` define to specify how many cycles to wait before sampling the I/O pin, by default it is approximately 20ms in a 72Mhz MCU. +On the bluepill boards, the default is using the BOOT1 input (available on jumper in the board) to enter the bootloader. + ### WebUSB This bootloader implements the draft [WebUSB](https://wicg.github.io/webusb/) specification, which allows web pages to access the bootloader (after presenting the user with a device picker dialog). diff --git a/release.Makefile b/release.Makefile index 8bc500f..af4c6bc 100644 --- a/release.Makefile +++ b/release.Makefile @@ -31,7 +31,13 @@ BUILD_DIR ?= ./build all: dapboot-bluepill.bin \ dapboot-maplemini.bin \ - dapboot-stlink.bin + dapboot-stlink.bin \ + dapboot-bluepill-high.bin \ + dapboot-maplemini-high.bin \ + dapboot-stlink-high.bin \ + dapboot-bluepill-high-128.bin \ + dapboot-maplemini-high-128.bin \ + dapboot-stlink-high-128.bin clean: $(Q)$(RM) $(BUILD_DIR)/*.bin @@ -59,3 +65,39 @@ dapboot-maplemini.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=MAPLEMINI -C src/ clean $(Q)$(MAKE) TARGET=MAPLEMINI -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepill-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-stlink-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=STLINK_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=STLINK_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-maplemini-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepill-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-stlink-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=STLINK_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=STLINK_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-maplemini-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) diff --git a/src/dapboot.c b/src/dapboot.c index a43a524..54e8599 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -27,12 +27,15 @@ #include "winusb.h" #include "config.h" +/* Used only in the high memory bootloader */ +extern volatile const vector_table_t vector_table; + static inline void __set_MSP(uint32_t topOfMainStack) { asm("msr msp, %0" : : "r" (topOfMainStack)); } bool validate_application(void) { - if ((*(volatile uint32_t *)APP_BASE_ADDRESS & 0x2FFE0000) == 0x20000000) { + if (((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000) { return true; } return false; @@ -41,19 +44,19 @@ bool validate_application(void) { static void jump_to_application(void) __attribute__ ((noreturn)); static void jump_to_application(void) { - vector_table_t* app_vector_table = (vector_table_t*)APP_BASE_ADDRESS; - + /* Use the application's vector table */ - target_relocate_vector_table(); + if (APP_RELOCATE_VECTORS) + target_relocate_vector_table(); /* Do any necessary early setup for the application */ target_pre_main(); /* Initialize the application's stack pointer */ - __set_MSP((uint32_t)(app_vector_table->initial_sp_value)); + __set_MSP((uint32_t)(APP_INITIAL_STACK)); /* Jump to the application entry point */ - app_vector_table->reset(); + APP_ENTRY_POINT(); while (1); } diff --git a/src/dapboot.h b/src/dapboot.h index 6c9e3ee..121571c 100644 --- a/src/dapboot.h +++ b/src/dapboot.h @@ -21,4 +21,22 @@ extern bool validate_application(void); +#ifdef BOOTLOADER_HIGH +/* Definitions for high memory bootloader */ +#define APP_INITIAL_STACK vector_table.reserved_x001c[0] +#define APP_ENTRY_POINT vector_table.reserved_x001c[1] +#define APP_RELOCATE_VECTORS 0 +#define DFU_PATCH_VECTORS 1 +#define BOOTLOADER_OFFSET 0 + +#else +/* Definitions for low memory (standard) bootloader */ +#define APP_INITIAL_STACK ((vector_table_t*)APP_BASE_ADDRESS)->initial_sp_value +#define APP_ENTRY_POINT ((vector_table_t*)APP_BASE_ADDRESS)->reset +#define APP_RELOCATE_VECTORS 1 +#define DFU_PATCH_VECTORS 0 +#define BOOTLOADER_OFFSET 0x00002000 + +#endif + #endif diff --git a/src/dfu.c b/src/dfu.c index 7351eb6..f17a3d7 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -94,6 +95,24 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data (void)usbd_dev; (void)req; + + if (DFU_PATCH_VECTORS && current_dfu_offset == 0) { + if (dfu_download_size < offsetof(vector_table_t, reserved_x001c[1])) { + /* Can't handle splitting the vector table right now */ + dfu_set_status(DFU_STATUS_ERR_VENDOR); + } else { + vector_table_t* app_vector_table = (vector_table_t*)dfu_download_buffer; + /* Stash the application's initial stack value and reset + pointer in unused vector table entries */ + app_vector_table->reserved_x001c[0] = (vector_table_entry_t)(app_vector_table->initial_sp_value); + app_vector_table->reserved_x001c[1] = app_vector_table->reset; + /* Overwrite the stack and reset pointer to run the + bootloader instead */ + app_vector_table->initial_sp_value = &_stack; + app_vector_table->reset = reset_handler; + } + } + const uint16_t* data = (uint16_t*)dfu_download_buffer; uint16_t* dest = (uint16_t*)(APP_BASE_ADDRESS + current_dfu_offset); @@ -234,7 +253,6 @@ static int dfu_control_class_request(usbd_device *usbd_dev, __attribute__ ((fallthrough)); } case STATE_DFU_UPLOAD_IDLE: { - *buf = (uint8_t*)(APP_BASE_ADDRESS + current_dfu_offset); uint16_t len_to_copy = req->wLength; size_t max_firmware_size = target_get_max_firmware_size(); if (current_dfu_offset + req->wLength > max_firmware_size) { @@ -244,6 +262,23 @@ static int dfu_control_class_request(usbd_device *usbd_dev, dfu_set_state(STATE_DFU_UPLOAD_IDLE); } *len = len_to_copy; + if (DFU_PATCH_VECTORS && current_dfu_offset < sizeof(vector_table_t)) { + /* Copy the flash memory to the download buffer, to + undo the vector modifications. */ + memcpy(dfu_download_buffer, (const void*)(APP_BASE_ADDRESS), + sizeof(dfu_download_buffer)); + vector_table_t* app_vector_table = (vector_table_t*)dfu_download_buffer; + /* Put the original stack pointer and reset vectors + back */ + app_vector_table->initial_sp_value = (unsigned int*)(app_vector_table->reserved_x001c[0]); + app_vector_table->reset = app_vector_table->reserved_x001c[1]; + app_vector_table->reserved_x001c[0] = 0; + app_vector_table->reserved_x001c[1] = 0; + /* Return the correct pointer */ + *buf = dfu_download_buffer + current_dfu_offset; + } else { + *buf = (uint8_t*)(APP_BASE_ADDRESS + current_dfu_offset); + } current_dfu_offset += len_to_copy; break; } diff --git a/src/stm32f103/bluepill/config.h b/src/stm32f103/bluepill/config.h index c0df9e0..1f64b7e 100644 --- a/src/stm32f103/bluepill/config.h +++ b/src/stm32f103/bluepill/config.h @@ -20,7 +20,7 @@ #define CONFIG_H_INCLUDED #ifndef APP_BASE_ADDRESS -#define APP_BASE_ADDRESS 0x08002000 +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) #endif #ifndef FLASH_SIZE_OVERRIDE #define FLASH_SIZE_OVERRIDE 0x20000 diff --git a/src/stm32f103/generic/config.h b/src/stm32f103/generic/config.h index 8bfe845..8b526e5 100644 --- a/src/stm32f103/generic/config.h +++ b/src/stm32f103/generic/config.h @@ -20,7 +20,7 @@ #define CONFIG_H_INCLUDED #ifndef APP_BASE_ADDRESS -#define APP_BASE_ADDRESS 0x08002000 +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) #endif #ifndef FLASH_SIZE_OVERRIDE #define FLASH_SIZE_OVERRIDE 0x20000 diff --git a/src/stm32f103/maplemini/config.h b/src/stm32f103/maplemini/config.h index f44ebc2..bdbb226 100644 --- a/src/stm32f103/maplemini/config.h +++ b/src/stm32f103/maplemini/config.h @@ -20,7 +20,7 @@ #define CONFIG_H_INCLUDED #ifndef APP_BASE_ADDRESS -#define APP_BASE_ADDRESS 0x08002000 +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) #endif #ifndef FLASH_PAGE_SIZE #define FLASH_PAGE_SIZE 1024 diff --git a/src/stm32f103/stlink/config.h b/src/stm32f103/stlink/config.h index b0127b4..c72f671 100644 --- a/src/stm32f103/stlink/config.h +++ b/src/stm32f103/stlink/config.h @@ -20,7 +20,7 @@ #define CONFIG_H_INCLUDED #ifndef APP_BASE_ADDRESS -#define APP_BASE_ADDRESS 0x08002000 +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) #endif #ifndef FLASH_SIZE_OVERRIDE #define FLASH_SIZE_OVERRIDE 0x20000 diff --git a/src/stm32f103/stm32f1.ld b/src/stm32f103/stm32f1.ld new file mode 100644 index 0000000..e209cbc --- /dev/null +++ b/src/stm32f103/stm32f1.ld @@ -0,0 +1,109 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Generic linker script for STM32 targets using libopencm3. */ + +/* Memory regions must be defined in the ld script which includes this one. */ + +/* Enforce emmition of the vector table. */ +EXTERN (vector_table) + +/* Define the entry point of the output file. */ +ENTRY(reset_handler) + +/* Define sections. */ +SECTIONS +{ + .vectors : { + *(.vectors) /* Vector table */ + } >vectors + + .text : { + *(.text*) /* Program code */ + . = ALIGN(4); + *(.rodata*) /* Read-only data */ + . = ALIGN(4); + } >rom + + /* C++ Static constructors/destructors, also used for __attribute__ + * ((constructor)) and the likes */ + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >rom + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >rom + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >rom + + /* + * Another section used by C++ stuff, appears when using newlib with + * 64bit (long long) printf support + */ + .ARM.extab : { + *(.ARM.extab*) + } >rom + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >rom + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) /* Read-write initialized data */ + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + + .bss : { + *(.bss*) /* Read-write zero initialized data */ + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /* + * The .eh_frame section appears to be used for C++ exception handling. + * You may need to fix this if you're using C++. + */ + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); + diff --git a/src/stm32f103/stm32f103x8.ld b/src/stm32f103/stm32f103x8.ld index 68645d8..507e5ea 100644 --- a/src/stm32f103/stm32f103x8.ld +++ b/src/stm32f103/stm32f103x8.ld @@ -17,15 +17,17 @@ * along with this library. If not, see . */ -/* Linker script for STM32F103x8, 64k flash, 20k RAM. */ +/* Linker script for STM32F103x8, 64k flash, 20k RAM. + * This script also works for the STM32F103xB, as the bootloader uses only the + * first 8kB of flash. */ /* Define memory regions. */ -/* 8k for the bootloader */ MEMORY { - rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x08000150, LENGTH = 0x1EB0 ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K } /* Include the common ld script. */ -INCLUDE libopencm3_stm32f1.ld +INCLUDE stm32f103/stm32f1.ld diff --git a/src/stm32f103/stm32f103x8_high.ld b/src/stm32f103/stm32f103x8_high.ld new file mode 100644 index 0000000..25fda4e --- /dev/null +++ b/src/stm32f103/stm32f103x8_high.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for STM32F103x8, 64k flash, 20k RAM. */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x0800e600, LENGTH = 0x1A00 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script. */ +INCLUDE stm32f103/stm32f1.ld diff --git a/src/stm32f103/stm32f103xb_high.ld b/src/stm32f103/stm32f103xb_high.ld new file mode 100644 index 0000000..59d048d --- /dev/null +++ b/src/stm32f103/stm32f103xb_high.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for STM32F103xB, 128k flash, 20k RAM. */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x0801e600, LENGTH = 0x1A00 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script. */ +INCLUDE stm32f103/stm32f1.ld diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 7c25942..ea380db 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -25,6 +25,7 @@ #include #include +#include "dapboot.h" #include "target.h" #include "config.h" #include "backup.h" diff --git a/src/targets.mk b/src/targets.mk index d93a11a..fd632a1 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -26,18 +26,60 @@ ifeq ($(TARGET),BLUEPILL) LDSCRIPT := ./stm32f103/stm32f103x8.ld ARCH = STM32F1 endif +ifeq ($(TARGET),BLUEPILL_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepill + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),BLUEPILL_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepill + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif ifeq ($(TARGET),MAPLEMINI) TARGET_COMMON_DIR := ./stm32f103 TARGET_SPEC_DIR := ./stm32f103/maplemini LDSCRIPT := ./stm32f103/stm32f103x8.ld ARCH = STM32F1 endif +ifeq ($(TARGET),MAPLEMINI_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/maplemini + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),MAPLEMINI_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/maplemini + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif ifeq ($(TARGET),STLINK) TARGET_COMMON_DIR := ./stm32f103 TARGET_SPEC_DIR := ./stm32f103/stlink LDSCRIPT := ./stm32f103/stm32f103x8.ld ARCH = STM32F1 endif +ifeq ($(TARGET),STLINK_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/stlink + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),STLINK_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/stlink + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif ifndef ARCH $(error Unknown target $(TARGET)) From 083f3afb9269c34c001ca38858a0d45b046e417f Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sun, 3 May 2020 17:04:46 -0700 Subject: [PATCH 18/66] Pass through custom toolchain PREFIX for locm3 Otherwise the sub-make will use the unmodified toolchain --- src/rules.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rules.mk b/src/rules.mk index 91ba674..0be75e1 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -159,6 +159,9 @@ $(LDSCRIPT): $(OPENCM3_DIR)/Makefile: $(Q)git submodule update --init $(OPENCM3_DIR) +# If overriding the toolchain, use it when building LOCM3 +export PREFIX + $(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/Makefile $(Q)$(MAKE) -C $(OPENCM3_DIR) AR=$(AR) CFLAGS="-flto -g" TARGETS=$(OPENCM3_TARGET) From 5879c2ee32d08a3b262a20845a88234d81d4955a Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sun, 3 May 2020 17:08:40 -0700 Subject: [PATCH 19/66] Fill bin file gaps with all ones instead of zeros No need to force the in-between flash to zero when we can leave them at their default erased state (all ones) --- src/rules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules.mk b/src/rules.mk index 0be75e1..448a9f2 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -172,7 +172,7 @@ locm3: $(LIB_DIR)/lib$(LIBNAME).a %.bin: %.elf @#printf " OBJCOPY $(*).bin\n" - $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + $(Q)$(OBJCOPY) -Obinary --gap-fill=0xff $(*).elf $(*).bin %.hex: %.elf @#printf " OBJCOPY $(*).hex\n" From d6dd44634d73eebc42a7a6530c2697b610323e4b Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sun, 3 May 2020 17:22:52 -0700 Subject: [PATCH 20/66] Differentiate product name for high-memory version of the bootloader --- src/usb_conf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/usb_conf.c b/src/usb_conf.c index 64e438c..62a834f 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -98,7 +98,11 @@ static char serial_number[USB_SERIAL_NUM_LENGTH+1]; static const char *usb_strings[] = { "Devanarchy", +#ifdef BOOTLOADER_HIGH + "DAPBoot High-Memory DFU Bootloader", +#else "DAPBoot DFU Bootloader", +#endif serial_number, "DAPBoot DFU" }; From c015c39b898d0986bd9f86fa9997dd5a02e883af Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sun, 3 May 2020 17:23:23 -0700 Subject: [PATCH 21/66] Bump version to 1.11 --- src/usb_conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usb_conf.c b/src/usb_conf.c index 62a834f..e6095a6 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -37,7 +37,7 @@ static const struct usb_device_descriptor dev = { .bMaxPacketSize0 = 64, .idVendor = USB_VID, .idProduct = USB_PID, - .bcdDevice = 0x0110, + .bcdDevice = 0x0111, .iManufacturer = 1, .iProduct = 2, .iSerialNumber = 3, From e047ca0b284beb963908c40259c1176f884907cb Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sun, 3 May 2020 17:28:29 -0700 Subject: [PATCH 22/66] Update CI toolchain from 2017q2 (GCC 6.2.1) to 2019q4 (9.2.1) --- util/install-toolchain.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/install-toolchain.sh b/util/install-toolchain.sh index f25f950..0b236df 100755 --- a/util/install-toolchain.sh +++ b/util/install-toolchain.sh @@ -1,7 +1,7 @@ #!/bin/bash set -eo pipefail -URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 -TOOLCHAIN=gcc-arm-none-eabi-6-2017-q2-update +URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2 +TOOLCHAIN=gcc-arm-none-eabi-9-2019-q4-major TOOLCHAINS=$HOME/toolchains TOOLCHAIN_MISSING=0 GCC=${TOOLCHAINS}/gcc-arm-embedded/bin/arm-none-eabi-gcc From b019c6e229e8de71f72a9855d6b4f7c8f4c71728 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sun, 3 May 2020 19:19:37 -0700 Subject: [PATCH 23/66] Update README board links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 745dec6..aab5d39 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ To build other targets, you can override the | Target Name | Description | Link | | ----------- | ----------- |----- | -|`BLUEPILL` | Cheap dev board | http://wiki.stm32duino.com/index.php?title=Blue_Pill | -|`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | http://wiki.stm32duino.com/index.php?title=Maple_Mini | +|`BLUEPILL` | Cheap dev board | https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Blue_Pill.html | +|`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Maple_Mini.html | |`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones | For each of the above targets, there are three variants that can be added to the target name: From 0fbb8a0e353ffe894d750c6e43bfad5a86097895 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 13 Oct 2020 12:21:10 +0000 Subject: [PATCH 24/66] dfu: allow overriding wTransferSize Some target implementations might like to use transfers that more closely match the target flash pages. Signed-off-by: Karl Palsson --- src/dfu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dfu.c b/src/dfu.c index f17a3d7..518b253 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -30,6 +30,10 @@ #include "dapboot.h" #include "config.h" +#ifndef TARGET_DFU_WTRANSFERSIZE +#define TARGET_DFU_WTRANSFERSIZE USB_CONTROL_BUF_SIZE +#endif + const struct usb_dfu_descriptor dfu_function = { .bLength = sizeof(struct usb_dfu_descriptor), .bDescriptorType = DFU_FUNCTIONAL, @@ -37,7 +41,7 @@ const struct usb_dfu_descriptor dfu_function = { (DFU_UPLOAD_AVAILABLE ? USB_DFU_CAN_UPLOAD : 0) | USB_DFU_WILL_DETACH ), .wDetachTimeout = 255, - .wTransferSize = USB_CONTROL_BUF_SIZE, + .wTransferSize = TARGET_DFU_WTRANSFERSIZE, .bcdDFUVersion = 0x0110, }; From 24d70456871a14fb3a4a7b98f086653f17f21ae0 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 13 Oct 2020 12:22:34 +0000 Subject: [PATCH 25/66] dfu: allow targets to customize poll interval The fixed 100ms poll interval is ~ok[1] when you are doing 1024 byte writes on an f1, where 1024/2 * 70usecs for the flashing, plus 40ms for the page erase is about 75ms total. However, on other targets (forthcoming) with (very) different flash programming speeds, we may want to use different poll intervals. Signed-off-by: Karl Palsson [1] Just dropping this to 75 straight away should give you a noticable, and very free flash speed up. --- src/dfu.c | 2 +- src/dummy.c | 6 ++++++ src/target.h | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dfu.c b/src/dfu.c index 518b253..383f5f5 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -169,7 +169,7 @@ static int dfu_control_class_request(usbd_device *usbd_dev, #if DFU_DOWNLOAD_AVAILABLE case STATE_DFU_DNLOAD_SYNC: { dfu_set_state(STATE_DFU_DNBUSY); - bwPollTimeout = 100; + bwPollTimeout = target_get_timeout(); *complete = &dfu_on_download_request; break; } diff --git a/src/dummy.c b/src/dummy.c index 6bf49fc..6589e57 100644 --- a/src/dummy.c +++ b/src/dummy.c @@ -26,6 +26,7 @@ void target_get_serial_number(char* dest, size_t max_chars) __attribute__((weak) void target_log(const char* str) __attribute__((weak)); void target_manifest_app(void) __attribute__((weak)); void target_pre_main(void) __attribute__((weak)); +size_t target_get_timeout(void) __attribute__((weak)); void target_get_serial_number(char* dest, size_t max_chars) { (void)max_chars; @@ -46,3 +47,8 @@ void target_pre_main(void) { } + +size_t target_get_timeout(void) +{ + return 100; +} diff --git a/src/target.h b/src/target.h index 20301de..7e671cf 100644 --- a/src/target.h +++ b/src/target.h @@ -37,4 +37,6 @@ extern void target_flash_lock(void); extern bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count); extern void target_pre_main(void); +extern size_t target_get_timeout(void); + #endif From 217cf4f5d73e18a569ddbf0874d9a3de45012d4b Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 13 Oct 2020 14:04:03 +0000 Subject: [PATCH 26/66] add stm32l1 target First, this _requires_ c6e89adda836a98fc from upstream, which fixes flash unlocking added to the libopencm3 portion. This adds a generic basic L1 target, with no buttons or LEDs, support only via the backup registers for communications. A couple of functions that are really part of libopencm3 have been inlined into the target_stm32l1.c file as there's a lot more work involved yet than just updating to latest upstream. stm32l1-base.ld is an exact copy of stm32f1.ld, it's really just the plain generic linker script base, but again, the libopencm3 fork here is too old at present, so it's simply been copied. stm32l1-standard.ld provides a basic top level ld script that suits all L1 parts, by limiting the ram to the minimum across all parts. (Still enough for the bootloader to run) The primary difference vs the f1 target is that the flashing must be done from a RAM function. Signed-off-by: Karl Palsson --- src/rules.mk | 8 ++ src/stm32l1/backup.c | 37 ++++++ src/stm32l1/backup.h | 33 +++++ src/stm32l1/generic/config.h | 22 ++++ src/stm32l1/stm32l1-base.ld | 109 ++++++++++++++++ src/stm32l1/stm32l1-standard.ld | 22 ++++ src/stm32l1/target_stm32l1.c | 212 ++++++++++++++++++++++++++++++++ src/targets.mk | 7 ++ 8 files changed, 450 insertions(+) create mode 100644 src/stm32l1/backup.c create mode 100644 src/stm32l1/backup.h create mode 100644 src/stm32l1/generic/config.h create mode 100644 src/stm32l1/stm32l1-base.ld create mode 100644 src/stm32l1/stm32l1-standard.ld create mode 100644 src/stm32l1/target_stm32l1.c diff --git a/src/rules.mk b/src/rules.mk index 448a9f2..f1c8eea 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -44,6 +44,14 @@ ifeq ($(ARCH),STM32F1) OOCD_BOARD ?= target/stm32f1x.cfg OPENCM3_TARGET = "stm32/f1" endif +ifeq ($(ARCH),STM32L1) + LIBNAME = opencm3_stm32l1 + DEFS += -DSTM32L1 + FP_FLAGS ?= -msoft-float + ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd + OOCD_BOARD ?= target/stm32l1.cfg + OPENCM3_TARGET = "stm32/l1" +endif LIBNAME ?= opencm3_stm32f0 DEFS ?= -DSTM32F0 diff --git a/src/stm32l1/backup.c b/src/stm32l1/backup.c new file mode 100644 index 0000000..5c98577 --- /dev/null +++ b/src/stm32l1/backup.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, Devan Lai + * 2020, Karl Palsson, ported to L1 + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "backup.h" + +void backup_write(enum BackupRegister reg, uint32_t value) +{ + rcc_periph_clock_enable(RCC_PWR); + pwr_disable_backup_domain_write_protect(); + RTC_BKPXR(reg) = value; + pwr_enable_backup_domain_write_protect(); +} + +uint32_t backup_read(enum BackupRegister reg) +{ + return RTC_BKPXR(reg); +} diff --git a/src/stm32l1/backup.h b/src/stm32l1/backup.h new file mode 100644 index 0000000..c2fe2b0 --- /dev/null +++ b/src/stm32l1/backup.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BACKUP_H_INCLUDED +#define BACKUP_H_INCLUDED + +enum BackupRegister { + BKP0 = 0, + BKP1, + BKP2, + BKP3, + BKP4, +}; + +extern void backup_write(enum BackupRegister reg, uint32_t value); +extern uint32_t backup_read(enum BackupRegister reg); + +#endif diff --git a/src/stm32l1/generic/config.h b/src/stm32l1/generic/config.h new file mode 100644 index 0000000..bffbf40 --- /dev/null +++ b/src/stm32l1/generic/config.h @@ -0,0 +1,22 @@ +/* + * 2020 - Karl Palsson + * Considered to be released into the public domain, or where not available + * under your choice of the following spdx identifiers: + * MIT, ISC, Apache-2.0, BSD-1-Clause, BSD-2-Clause, BSD-3-Clause, + * CC-BY-4.0, GPL-2.0-or-later, LGPL-2.0-or-later + * Pick whatever makes your integration life easier + * + * defauly config.h settings + */ + +#pragma once + +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#define FLASH_PAGE_SIZE 128 +#define DFU_UPLOAD_AVAILABLE 1 +#define DFU_DOWNLOAD_AVAILABLE 1 +#define TARGET_DFU_WTRANSFERSIZE 128 + +#define HAVE_LED 0 +#define HAVE_BUTTON 0 +#define HAVE_USB_PULLUP_CONTROL 0 diff --git a/src/stm32l1/stm32l1-base.ld b/src/stm32l1/stm32l1-base.ld new file mode 100644 index 0000000..e209cbc --- /dev/null +++ b/src/stm32l1/stm32l1-base.ld @@ -0,0 +1,109 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Generic linker script for STM32 targets using libopencm3. */ + +/* Memory regions must be defined in the ld script which includes this one. */ + +/* Enforce emmition of the vector table. */ +EXTERN (vector_table) + +/* Define the entry point of the output file. */ +ENTRY(reset_handler) + +/* Define sections. */ +SECTIONS +{ + .vectors : { + *(.vectors) /* Vector table */ + } >vectors + + .text : { + *(.text*) /* Program code */ + . = ALIGN(4); + *(.rodata*) /* Read-only data */ + . = ALIGN(4); + } >rom + + /* C++ Static constructors/destructors, also used for __attribute__ + * ((constructor)) and the likes */ + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >rom + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >rom + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >rom + + /* + * Another section used by C++ stuff, appears when using newlib with + * 64bit (long long) printf support + */ + .ARM.extab : { + *(.ARM.extab*) + } >rom + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >rom + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) /* Read-write initialized data */ + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + + .bss : { + *(.bss*) /* Read-write zero initialized data */ + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /* + * The .eh_frame section appears to be used for C++ exception handling. + * You may need to fix this if you're using C++. + */ + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); + diff --git a/src/stm32l1/stm32l1-standard.ld b/src/stm32l1/stm32l1-standard.ld new file mode 100644 index 0000000..9dfe0d3 --- /dev/null +++ b/src/stm32l1/stm32l1-standard.ld @@ -0,0 +1,22 @@ +/* + * 2020 - Karl Palsson + * Considered to be released into the public domain, or where not available + * under your choice of the following spdx identifiers: + * MIT, ISC, Apache-2.0, BSD-1-Clause, BSD-2-Clause, BSD-3-Clause, + * CC-BY-4.0, GPL-2.0-or-later, LGPL-2.0-or-later + * Pick whatever makes your integration life easier + * + * Linker script for STM32L1 parts. Bootloader only for 8k, and all parts + * have at least 10k RAM, (-A parts have 32K RAM, but 10 is enough) + */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x08000150, LENGTH = 0x1EB0 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 10K +} + +/* Include the common ld script. */ +INCLUDE stm32l1/stm32l1-base.ld diff --git a/src/stm32l1/target_stm32l1.c b/src/stm32l1/target_stm32l1.c new file mode 100644 index 0000000..bf22dfd --- /dev/null +++ b/src/stm32l1/target_stm32l1.c @@ -0,0 +1,212 @@ +/* + * 2020 - Karl Palsson + * Considered to be released into the public domain, or where not available + * under your choice of the following spdx identifiers: + * MIT, ISC, Apache-2.0, BSD-1-Clause, BSD-2-Clause, BSD-3-Clause, + * CC-BY-4.0, GPL-2.0-or-later, LGPL-2.0-or-later + * Pick whatever makes your integration life easier + * + * STM32L1 generic target side portions for dapboot. + * There's no "standard" l1 board, so you're always going + * to want to edit this... + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dapboot.h" +#include "target.h" +#include "config.h" +#include "backup.h" + +//#define CMD_FAST_BOOT 0xfa57b007 +static const uint32_t CMD_BOOT = 0x544F4F42UL; + +void target_clock_setup(void) { + +//#define MODERN_LOCM3 +#ifdef MODERN_LOCM3 + /* Clock struct for "any" board with a 16Mhz crystal */ + const struct rcc_clock_scale myclock_16m_hse = { + .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, + .pll_mul = RCC_CFGR_PLLMUL_MUL6, + .pll_div = RCC_CFGR_PLLDIV_DIV3, + .hpre = RCC_CFGR_HPRE_SYSCLK_NODIV, + .ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV, + .ppre2 = RCC_CFGR_PPRE2_HCLK_NODIV, + .voltage_scale = PWR_SCALE1, + .flash_waitstates = 1, + .ahb_frequency = 32e6, + .apb1_frequency = 32e6, + .apb2_frequency = 32e6, + }; +#else + const struct rcc_clock_scale myclock_16m_hse = { + .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, + .pll_mul = RCC_CFGR_PLLMUL_MUL6, + .pll_div = RCC_CFGR_PLLDIV_DIV3, + .hpre = RCC_CFGR_HPRE_SYSCLK_NODIV, + .ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV, + .ppre2 = RCC_CFGR_PPRE2_HCLK_NODIV, + .voltage_scale = PWR_SCALE1, + .flash_config = FLASH_ACR_LATENCY_1WS, + .ahb_frequency = 32000000, + .apb1_frequency = 32000000, + .apb2_frequency = 32000000, + }; +#endif + + rcc_clock_setup_pll(&myclock_16m_hse); +} + +void target_gpio_setup(void) +{ + /* TODO: if you need buttons or gpios, turn them on here */ +#if HAVE_LED +#endif +#if HAVE_BUTTON +#endif +} + +const usbd_driver* target_usb_init(void) +{ + rcc_periph_reset_pulse(RST_USB); + + /* Enable built in USB pullup on L1, note, this is out of spec on older revs! */ + rcc_periph_clock_enable(RCC_SYSCFG); +/* Compat for old library */ +#ifndef SYSCFG_PMC_USB_PU +#define SYSCFG_PMC_USB_PU (1<<0) +#endif + SYSCFG_PMC |= SYSCFG_PMC_USB_PU; + + return &st_usbfs_v1_usb_driver; +} + +/* This implementation will always start in bootloader, unless the app + * has asked it to skipp straight forwards + * You may wish to fill in button handling... + */ +bool target_get_force_bootloader(void) +{ + bool enter_bl = false; + uint32_t cmd = backup_read(BKP0); + if (cmd == CMD_BOOT) { + enter_bl = true; + } + backup_write(BKP0, 0); + +#if HAVE_BUTTON +#warning HAVE_BUTTON not implemented for L1 +#endif + + return enter_bl; +} + +void target_get_serial_number(char* dest, size_t max_chars) +{ + /* TODO, but not available in old locm3... + assert(max_chars > 6); + desig_get_unique_id_as_dfu(dest); + */ + desig_get_unique_id_as_string(dest, max_chars+1); +} + +size_t target_get_max_firmware_size(void) +{ + /* L1, unlike magical fairy land F1, tells it like it is */ + /* mask is working around bug upstream for L1 */ + size_t flash = desig_get_flash_size() & 0xff; + size_t total = flash * 1024; +#ifdef BOOTLOADER_HIGH +#error bootloader high memory mode not supported on L1 +#endif + return total - BOOTLOADER_OFFSET; +} + + +#ifndef MODERN_LOCM3 +/* These two are proposed upstream, but we need them here now */ +static void flash_erase_page(uint32_t page_address) +{ + FLASH_PECR |= FLASH_PECR_ERASE | FLASH_PECR_PROG; + /* L1 requires first word in page, L0 doesn't care, user take care */ + MMIO32(page_address) = 0; + while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY); + FLASH_PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_PROG); +} + +/* Must be run from RAM (per ref manual), and because it's in ram, more + * than 64MB away from flash address space, must be a long_call. + * (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78903 for noinline) */ +void flash_program_half_page(uint32_t *dst, const uint32_t *buf); +__attribute__ ((noinline, long_call, section (".data.ramfunctions"))) +void flash_program_half_page(uint32_t *dst, const uint32_t *buf) +{ + const uint32_t *src = buf; + + /* Enable half page writes to program memory */ + FLASH_PECR |= FLASH_PECR_FPRG | FLASH_PECR_PROG; + while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY); +#ifndef FLASH_HALF_PAGE_SIZE +#define FLASH_HALF_PAGE_SIZE 32 /* For L1, 16 for L0 */ +#endif + for (int i = 0; i < FLASH_HALF_PAGE_SIZE; i++) { + *dst++ = *src++; + } + while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY); + FLASH_PECR &= ~(FLASH_PECR_FPRG | FLASH_PECR_PROG); +} +#endif + +static bool target_flash_program_array_nice(uint32_t dest, const uint32_t* data, size_t word_count) +{ + /* whole page was erased, you're doing the full half page */ + (void)word_count; + assert(word_count <= 32); + if ((dest & (256-1)) == 0) { + flash_erase_page(dest); + } + cm_disable_interrupts(); + flash_program_half_page((uint32_t *)dest, data); + cm_enable_interrupts(); + return true; +} + +/** + * The API is (forrealz?!) tied to the F1 16bit halfwords! + * backconvert, and call a "nicer" api + */ +bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count) +{ + return target_flash_program_array_nice((uint32_t)dest, (const uint32_t *)data, half_word_count / 2); +} + +/* L1 does shorter transfers, and writes half pages at a time, + * can/should be polled more frequently */ +size_t target_get_timeout(void) +{ + return 5; +} + +/* this is all common code below, extract for upstream? */ +void target_relocate_vector_table(void) { + SCB_VTOR = APP_BASE_ADDRESS & 0xFFFF; +} + +void target_flash_unlock(void) { + flash_unlock(); +} + +void target_flash_lock(void) { + flash_lock(); +} + diff --git a/src/targets.mk b/src/targets.mk index fd632a1..e461b7b 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -80,6 +80,13 @@ ifeq ($(TARGET),STLINK_HIGH_128) ARCH = STM32F1 DEFS += -DBOOTLOADER_HIGH endif +ifeq ($(TARGET),STM32L1_GENERIC) + TARGET_COMMON_DIR := ./stm32l1 + TARGET_SPEC_DIR := ./stm32l1/generic + LDSCRIPT := ./stm32l1/stm32l1-standard.ld + ARCH = STM32L1 + DEFS += -DNDEBUG +endif ifndef ARCH $(error Unknown target $(TARGET)) From c8c3a2f7d543297ccf5de6f6fe1377067ee9d4f5 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 13 Oct 2020 15:50:53 +0000 Subject: [PATCH 27/66] dfu: support "detach" When in DFU mode, the only way to exit was via a download operation. "dfu-util -e" to request a detach was simply not supported, so the bootloader simply remains sitting there Handle detach via reboot, the usb layer will re-enumerate if it can, and will boot app/bl based on it's own decisions. Signed-off-by: Karl Palsson --- src/dfu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dfu.c b/src/dfu.c index 383f5f5..cebcfc5 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -294,6 +295,11 @@ static int dfu_control_class_request(usbd_device *usbd_dev, } #endif case DFU_DETACH: + /* Best we can do! */ + scb_reset_system(); + status = USBD_REQ_HANDLED; + break; + default: { /* Stall the control pipe */ dfu_set_status(DFU_STATUS_ERR_STALLEDPKT); From c87c047ecea09000dce4e99746d67ce42e1f3e63 Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 13 Oct 2020 22:44:17 +0000 Subject: [PATCH 28/66] l1: avoid warnings on "abuse" of .data subsections Somewhere, gcc probably has a footnote about how .data* is treated specially, but I can't find it. Introduce a special section that doesn't start with .data and presto, the assembler warnings go away. --- src/stm32l1/stm32l1-base.ld | 1 + src/stm32l1/target_stm32l1.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stm32l1/stm32l1-base.ld b/src/stm32l1/stm32l1-base.ld index e209cbc..889b71a 100644 --- a/src/stm32l1/stm32l1-base.ld +++ b/src/stm32l1/stm32l1-base.ld @@ -83,6 +83,7 @@ SECTIONS .data : { _data = .; *(.data*) /* Read-write initialized data */ + *(.ramtext*) /* code, that needs to be in ram */ . = ALIGN(4); _edata = .; } >ram AT >rom diff --git a/src/stm32l1/target_stm32l1.c b/src/stm32l1/target_stm32l1.c index bf22dfd..d85c68d 100644 --- a/src/stm32l1/target_stm32l1.c +++ b/src/stm32l1/target_stm32l1.c @@ -148,7 +148,7 @@ static void flash_erase_page(uint32_t page_address) * than 64MB away from flash address space, must be a long_call. * (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78903 for noinline) */ void flash_program_half_page(uint32_t *dst, const uint32_t *buf); -__attribute__ ((noinline, long_call, section (".data.ramfunctions"))) +__attribute__ ((noinline, long_call, section (".ramtext"))) void flash_program_half_page(uint32_t *dst, const uint32_t *buf) { const uint32_t *src = buf; From 2b3bf0b4281e8a6be157df67ada22269c9e7bf7e Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sat, 17 Oct 2020 17:59:27 +0000 Subject: [PATCH 29/66] Update to rebased latest version of locm3 Use enum usbd_request_return_codes type in control request handlers Use standard externs for _stack and vector_table Use desig_get_flash_size() in place of reading DESIG_FLASH_SIZE directly Use "modern" clock setup for STM32L1 Use PREFIX with embedded hyphen to match updated locm3 convention --- .circleci/config.yml | 2 +- libopencm3 | 2 +- src/Makefile | 2 +- src/dapboot.c | 3 --- src/dfu.c | 13 ++++++------- src/rules.mk | 20 ++++++++++---------- src/stm32f103/target_stm32f103.c | 2 +- src/stm32l1/target_stm32l1.c | 16 ---------------- src/webusb.c | 11 ++++++----- src/winusb.c | 11 ++++++----- 10 files changed, 32 insertions(+), 50 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 01c3d83..a17319c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,6 +20,6 @@ jobs: name: Compile firmware command: make -f release.Makefile -k all environment: - PREFIX: "~/toolchains/gcc-arm-embedded/bin/arm-none-eabi" + PREFIX: "~/toolchains/gcc-arm-embedded/bin/arm-none-eabi-" - store_artifacts: path: build/ diff --git a/libopencm3 b/libopencm3 index 90be020..b8b8d41 160000 --- a/libopencm3 +++ b/libopencm3 @@ -1 +1 @@ -Subproject commit 90be02008e6b651ce737019b6b39ddfdf85eda6b +Subproject commit b8b8d416d4596d9b3de5962b800f0cbf138bf3b1 diff --git a/src/Makefile b/src/Makefile index bb68e9b..caff1b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,7 +38,7 @@ clean:: include rules.mk size: $(OBJS) $(BINARY).elf - @$(PREFIX)-size $(OBJS) $(BINARY).elf + @$(PREFIX)size $(OBJS) $(BINARY).elf debug: $(BINARY).elf -$(GDB) --tui --eval "target remote | $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) -f debug.cfg" $(BINARY).elf diff --git a/src/dapboot.c b/src/dapboot.c index 54e8599..a96c483 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -27,9 +27,6 @@ #include "winusb.h" #include "config.h" -/* Used only in the high memory bootloader */ -extern volatile const vector_table_t vector_table; - static inline void __set_MSP(uint32_t topOfMainStack) { asm("msr msp, %0" : : "r" (topOfMainStack)); } diff --git a/src/dfu.c b/src/dfu.c index 383f5f5..de47c69 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -93,8 +93,6 @@ static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_dat dfu_set_state(STATE_DFU_MANIFEST_SYNC); } -extern unsigned _stack; - static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -144,15 +142,16 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data } } -static int dfu_control_class_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { +static enum usbd_request_return_codes +dfu_control_class_request(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback* complete) { if (req->wIndex != INTF_DFU) { return USBD_REQ_NEXT_CALLBACK; } - int status = USBD_REQ_HANDLED; + enum usbd_request_return_codes status = USBD_REQ_HANDLED; switch (req->bRequest) { case DFU_GETSTATE: { struct dfu_getstate_response* resp; diff --git a/src/rules.mk b/src/rules.mk index f1c8eea..9fe945e 100644 --- a/src/rules.mk +++ b/src/rules.mk @@ -81,16 +81,16 @@ OOCD_BOARD ?= target/stm32f1x.cfg #################################################################### # Executables -PREFIX ?= arm-none-eabi - -CC := $(PREFIX)-gcc -CXX := $(PREFIX)-g++ -LD := $(PREFIX)-gcc -AR := $(PREFIX)-gcc-ar -AS := $(PREFIX)-as -OBJCOPY := $(PREFIX)-objcopy -OBJDUMP := $(PREFIX)-objdump -GDB := $(PREFIX)-gdb +PREFIX ?= arm-none-eabi- + +CC := $(PREFIX)gcc +CXX := $(PREFIX)g++ +LD := $(PREFIX)gcc +AR := $(PREFIX)gcc-ar +AS := $(PREFIX)as +OBJCOPY := $(PREFIX)objcopy +OBJDUMP := $(PREFIX)objdump +GDB := $(PREFIX)gdb STFLASH = $(shell which st-flash) #################################################################### diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index ea380db..6948482 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -199,7 +199,7 @@ static uint16_t* get_flash_end(void) { return (uint16_t*)(FLASH_BASE + FLASH_SIZE_OVERRIDE); #else /* Only allow access to the chip's self-reported flash size */ - return (uint16_t*)(FLASH_BASE + (size_t)DESIG_FLASH_SIZE*FLASH_PAGE_SIZE); + return (uint16_t*)(FLASH_BASE + ((size_t)desig_get_flash_size())*1024); #endif } diff --git a/src/stm32l1/target_stm32l1.c b/src/stm32l1/target_stm32l1.c index bf22dfd..3e485f1 100644 --- a/src/stm32l1/target_stm32l1.c +++ b/src/stm32l1/target_stm32l1.c @@ -33,7 +33,6 @@ static const uint32_t CMD_BOOT = 0x544F4F42UL; void target_clock_setup(void) { //#define MODERN_LOCM3 -#ifdef MODERN_LOCM3 /* Clock struct for "any" board with a 16Mhz crystal */ const struct rcc_clock_scale myclock_16m_hse = { .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, @@ -48,21 +47,6 @@ void target_clock_setup(void) { .apb1_frequency = 32e6, .apb2_frequency = 32e6, }; -#else - const struct rcc_clock_scale myclock_16m_hse = { - .pll_source = RCC_CFGR_PLLSRC_HSE_CLK, - .pll_mul = RCC_CFGR_PLLMUL_MUL6, - .pll_div = RCC_CFGR_PLLDIV_DIV3, - .hpre = RCC_CFGR_HPRE_SYSCLK_NODIV, - .ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV, - .ppre2 = RCC_CFGR_PPRE2_HCLK_NODIV, - .voltage_scale = PWR_SCALE1, - .flash_config = FLASH_ACR_LATENCY_1WS, - .ahb_frequency = 32000000, - .apb1_frequency = 32000000, - .apb2_frequency = 32000000, - }; -#endif rcc_clock_setup_pll(&myclock_16m_hse); } diff --git a/src/webusb.c b/src/webusb.c index 010ff6c..72c37f3 100644 --- a/src/webusb.c +++ b/src/webusb.c @@ -50,10 +50,11 @@ static const struct webusb_url_descriptor landing_url_descriptor = { .URL = LANDING_PAGE_URL }; -static int webusb_control_vendor_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { +static enum usbd_request_return_codes +webusb_control_vendor_request(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback* complete) { (void)complete; (void)usbd_dev; @@ -61,7 +62,7 @@ static int webusb_control_vendor_request(usbd_device *usbd_dev, return USBD_REQ_NEXT_CALLBACK; } - int status = USBD_REQ_NOTSUPP; + enum usbd_request_return_codes status = USBD_REQ_NOTSUPP; switch (req->wIndex) { case WEBUSB_REQ_GET_URL: { if (req->wValue != 1) { diff --git a/src/winusb.c b/src/winusb.c index f009500..26aeb0d 100644 --- a/src/winusb.c +++ b/src/winusb.c @@ -39,10 +39,11 @@ static const struct winusb_compatible_id_descriptor winusb_wcid = { } }; -static int winusb_control_vendor_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { +static enum usbd_request_return_codes +winusb_control_vendor_request(usbd_device *usbd_dev, + struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback* complete) { (void)complete; (void)usbd_dev; @@ -50,7 +51,7 @@ static int winusb_control_vendor_request(usbd_device *usbd_dev, return USBD_REQ_NEXT_CALLBACK; } - int status = USBD_REQ_NOTSUPP; + enum usbd_request_return_codes status = USBD_REQ_NOTSUPP; if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { *buf = (uint8_t*)(&winusb_wcid); From 1b9169944ce9c1df4d75a35506a3b65715a7b9f1 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sat, 17 Oct 2020 12:52:12 -0700 Subject: [PATCH 30/66] dfu: defer detach reset to after USB response Let the USB stack respond so that dfu-util knows the detach request was accepted. --- src/dfu.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index 6885c03..640a2a2 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -94,6 +94,14 @@ static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_dat dfu_set_state(STATE_DFU_MANIFEST_SYNC); } +static void dfu_on_detach_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { + (void)usbd_dev; + (void)req; + + /* Reset and maybe launch the application */ + scb_reset_system(); +} + static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -293,11 +301,12 @@ dfu_control_class_request(usbd_device *usbd_dev, break; } #endif - case DFU_DETACH: - /* Best we can do! */ - scb_reset_system(); + + case DFU_DETACH: { + *complete = &dfu_on_detach_complete; status = USBD_REQ_HANDLED; break; + } default: { /* Stall the control pipe */ From eb141886befd1bcf9190a6cf502472877a24cc05 Mon Sep 17 00:00:00 2001 From: Jean THOMAS <25333063+jeanthom@users.noreply.github.com> Date: Mon, 5 Apr 2021 13:07:45 +0200 Subject: [PATCH 31/66] Add support for Olimex STM32-H103 --- README.md | 1 + release.Makefile | 23 ++++++- src/stm32f103/olimexstm32h103/config.h | 83 ++++++++++++++++++++++++++ src/targets.mk | 20 +++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/stm32f103/olimexstm32h103/config.h diff --git a/README.md b/README.md index aab5d39..52338ac 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ To build other targets, you can override the |`BLUEPILL` | Cheap dev board | https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Blue_Pill.html | |`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Maple_Mini.html | |`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones | +|`OLIMEXSTM32H103` | Olimex STM32-H103 | https://www.olimex.com/Products/ARM/ST/STM32-H103/ | For each of the above targets, there are three variants that can be added to the target name: diff --git a/release.Makefile b/release.Makefile index af4c6bc..ec1fe7a 100644 --- a/release.Makefile +++ b/release.Makefile @@ -32,12 +32,15 @@ BUILD_DIR ?= ./build all: dapboot-bluepill.bin \ dapboot-maplemini.bin \ dapboot-stlink.bin \ + dapboot-olimexstm32h103.bin \ dapboot-bluepill-high.bin \ dapboot-maplemini-high.bin \ dapboot-stlink-high.bin \ + dapboot-olimexstm32h103-high.bin \ dapboot-bluepill-high-128.bin \ dapboot-maplemini-high-128.bin \ - dapboot-stlink-high-128.bin + dapboot-stlink-high-128.bin \ + dapboot-olimexstm32h103-high-128.bin clean: $(Q)$(RM) $(BUILD_DIR)/*.bin @@ -66,6 +69,12 @@ dapboot-maplemini.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=MAPLEMINI -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) +dapboot-olimexstm32h103.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103 -C src/ clean + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + dapboot-bluepill-high.bin: | $(BUILD_DIR) @printf " BUILD $(@)\n" $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ clean @@ -84,6 +93,12 @@ dapboot-maplemini-high.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) +dapboot-olimexstm32h103-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + dapboot-bluepill-high-128.bin: | $(BUILD_DIR) @printf " BUILD $(@)\n" $(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/ clean @@ -101,3 +116,9 @@ dapboot-maplemini-high-128.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/ clean $(Q)$(MAKE) TARGET=MAPLEMINI_HIGH_128 -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-olimexstm32h103-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) diff --git a/src/stm32f103/olimexstm32h103/config.h b/src/stm32f103/olimexstm32h103/config.h new file mode 100644 index 0000000..b418991 --- /dev/null +++ b/src/stm32f103/olimexstm32h103/config.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021, Jean THOMAS + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOC +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO14 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 1 +#endif + +/* Technically, there is a button on PB8, but the button is + also shorted to BOOT0, so it's not very useful for us to + sample PB8 on boot, since pulling it high will already + trigger the ROM serial bootloader and prevent us from + running anyways. */ +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 0 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 1 +#endif +#ifndef USB_PULLUP_GPIO_PORT +#define USB_PULLUP_GPIO_PORT GPIOC +#endif +#ifndef USB_PULLUP_GPIO_PIN +#define USB_PULLUP_GPIO_PIN GPIO11 +#endif +#ifndef USB_PULLUP_ACTIVE_HIGH +#define USB_PULLUP_ACTIVE_HIGH 0 +#endif +#ifndef USB_PULLUP_OPEN_DRAIN +#define USB_PULLUP_OPEN_DRAIN 0 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 0 +#endif +#ifndef USES_GPIOB +#define USES_GPIOB 0 +#endif +#ifndef USES_GPIOC +#define USES_GPIOC 1 +#endif + +#endif diff --git a/src/targets.mk b/src/targets.mk index e461b7b..feecb2f 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -80,6 +80,26 @@ ifeq ($(TARGET),STLINK_HIGH_128) ARCH = STM32F1 DEFS += -DBOOTLOADER_HIGH endif +ifeq ($(TARGET),OLIMEXSTM32H103) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/olimexstm32h103 + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),OLIMEXSTM32H103_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/olimexstm32h103 + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),OLIMEXSTM32H103_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/olimexstm32h103 + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif ifeq ($(TARGET),STM32L1_GENERIC) TARGET_COMMON_DIR := ./stm32l1 TARGET_SPEC_DIR := ./stm32l1/generic From 099d8e80699167ece78ab8b5ff2135421f1284b7 Mon Sep 17 00:00:00 2001 From: Jean THOMAS <25333063+jeanthom@users.noreply.github.com> Date: Tue, 6 Apr 2021 07:40:08 +0200 Subject: [PATCH 32/66] Implement button for Olimex STM32-H103 --- src/stm32f103/olimexstm32h103/config.h | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/stm32f103/olimexstm32h103/config.h b/src/stm32f103/olimexstm32h103/config.h index b418991..b33f8f6 100644 --- a/src/stm32f103/olimexstm32h103/config.h +++ b/src/stm32f103/olimexstm32h103/config.h @@ -45,13 +45,24 @@ #define LED_OPEN_DRAIN 1 #endif -/* Technically, there is a button on PB8, but the button is - also shorted to BOOT0, so it's not very useful for us to - sample PB8 on boot, since pulling it high will already - trigger the ROM serial bootloader and prevent us from - running anyways. */ +/* "BUT" button on on PA0, pulled high by R18 */ #ifndef HAVE_BUTTON -#define HAVE_BUTTON 0 +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 0 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO0 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 0 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 #endif #ifndef HAVE_USB_PULLUP_CONTROL @@ -71,7 +82,7 @@ #endif #ifndef USES_GPIOA -#define USES_GPIOA 0 +#define USES_GPIOA 1 #endif #ifndef USES_GPIOB #define USES_GPIOB 0 From 87878950d3bd94442d768106a112c0aa0dcf3a7c Mon Sep 17 00:00:00 2001 From: Jean THOMAS <25333063+jeanthom@users.noreply.github.com> Date: Tue, 6 Apr 2021 07:42:29 +0200 Subject: [PATCH 33/66] Fix LED pin for Olimex STM32-H103 --- src/stm32f103/olimexstm32h103/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stm32f103/olimexstm32h103/config.h b/src/stm32f103/olimexstm32h103/config.h index b33f8f6..56a7a56 100644 --- a/src/stm32f103/olimexstm32h103/config.h +++ b/src/stm32f103/olimexstm32h103/config.h @@ -39,7 +39,7 @@ #define LED_GPIO_PORT GPIOC #endif #ifndef LED_GPIO_PIN -#define LED_GPIO_PIN GPIO14 +#define LED_GPIO_PIN GPIO12 #endif #ifndef LED_OPEN_DRAIN #define LED_OPEN_DRAIN 1 From 2189eca655e5787fbc6ae23fde16c6d06f20b744 Mon Sep 17 00:00:00 2001 From: Jean THOMAS <25333063+jeanthom@users.noreply.github.com> Date: Tue, 6 Apr 2021 07:44:34 +0200 Subject: [PATCH 34/66] Use open drain for USB pull-up control in Olimex STM32-H103 --- src/stm32f103/olimexstm32h103/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stm32f103/olimexstm32h103/config.h b/src/stm32f103/olimexstm32h103/config.h index 56a7a56..23baf06 100644 --- a/src/stm32f103/olimexstm32h103/config.h +++ b/src/stm32f103/olimexstm32h103/config.h @@ -78,7 +78,7 @@ #define USB_PULLUP_ACTIVE_HIGH 0 #endif #ifndef USB_PULLUP_OPEN_DRAIN -#define USB_PULLUP_OPEN_DRAIN 0 +#define USB_PULLUP_OPEN_DRAIN 1 #endif #ifndef USES_GPIOA From 1a92de6cec4937bc512ab5ad522489eb1b8899db Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Tue, 13 Jul 2021 14:29:12 -0700 Subject: [PATCH 35/66] Allow configuration of VID/PID without hacking up the source code --- README.md | 7 ++++++- src/usb_conf.h | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 52338ac..fd7ff2d 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,12 @@ This bootloader implements the draft [WebUSB](https://wicg.github.io/webusb/) sp For a demo implementing dfu-util features in the browser, see https://devanlai.github.io/webdfu/dfu-util/ ## USB VID/PID -The USB VID/PID pair ([1209/DB42](http://pid.codes/1209/DB42/)) is allocated through the [pid.codes](http://pid.codes/) open-source USB PID program. +The default USB VID/PID pair ([1209/DB42](http://pid.codes/1209/DB42/)) is allocated through the [pid.codes](http://pid.codes/) open-source USB PID program. + +To use a custom VID/PID pair, you need to set the macros `USB_VID` and `USB_PID`. One way to do this is by setting the `DEFS` environment variable when compiling: + + DEFS="-DUSB_VID=0x1209 -DUSB_PID=0xCAFE" make + ## Licensing All contents of the dapboot project are licensed under terms that are compatible with the terms of the GNU Lesser General Public License version 3. diff --git a/src/usb_conf.h b/src/usb_conf.h index 3e6f9b0..441e2dd 100644 --- a/src/usb_conf.h +++ b/src/usb_conf.h @@ -21,8 +21,14 @@ #include +#ifndef USB_VID #define USB_VID 0x1209 +#endif + +#ifndef USB_PID #define USB_PID 0xdb42 +#endif + #define USB_CONTROL_BUF_SIZE 1024 #define USB_SERIAL_NUM_LENGTH 24 #define INTF_DFU 0 From 9e2fd01fb4ad2f607657d664a76044949a136aef Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Tue, 13 Jul 2021 14:59:39 -0700 Subject: [PATCH 36/66] Document how to customize the DFU endpoint URL --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 52338ac..17807e6 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,12 @@ This bootloader implements the draft [WebUSB](https://wicg.github.io/webusb/) sp For a demo implementing dfu-util features in the browser, see https://devanlai.github.io/webdfu/dfu-util/ +To customize the WebUSB landing page, you can use the `LANDING_PAGE_URL` define. To set it from the command line, you can use the `DEFS` environment variable: + + DEFS='-DLANDING_PAGE_URL=\"example.com/dfu-util/\"' make + +Note that the URL scheme shoul not be part of the `LANDING_PAGE_URL` string. As of this writing, it is hardcoded to HTTPS. + ## USB VID/PID The USB VID/PID pair ([1209/DB42](http://pid.codes/1209/DB42/)) is allocated through the [pid.codes](http://pid.codes/) open-source USB PID program. From bff90e4374b0003e6a4ece53d86a27c4be90e49a Mon Sep 17 00:00:00 2001 From: iovxw Date: Sun, 15 Aug 2021 01:39:32 +0800 Subject: [PATCH 37/66] Add Bluepill Plus support --- README.md | 1 + release.Makefile | 23 +++++++- src/stm32f103/bluepillplus/config.h | 81 +++++++++++++++++++++++++++++ src/targets.mk | 19 +++++++ 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/stm32f103/bluepillplus/config.h diff --git a/README.md b/README.md index a706dab..8045133 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ To build other targets, you can override the |`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Maple_Mini.html | |`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones | |`OLIMEXSTM32H103` | Olimex STM32-H103 | https://www.olimex.com/Products/ARM/ST/STM32-H103/ | +|`BLUEPILLPLUSSTM32` | Bluepill with USB C | https://github.com/WeActTC/BluePill-Plus/ | For each of the above targets, there are three variants that can be added to the target name: diff --git a/release.Makefile b/release.Makefile index ec1fe7a..b217af0 100644 --- a/release.Makefile +++ b/release.Makefile @@ -33,14 +33,17 @@ all: dapboot-bluepill.bin \ dapboot-maplemini.bin \ dapboot-stlink.bin \ dapboot-olimexstm32h103.bin \ + dapboot-bluepillplusstm32.bin \ dapboot-bluepill-high.bin \ dapboot-maplemini-high.bin \ dapboot-stlink-high.bin \ dapboot-olimexstm32h103-high.bin \ + dapboot-bluepillplusstm32-high.bin \ dapboot-bluepill-high-128.bin \ dapboot-maplemini-high-128.bin \ dapboot-stlink-high-128.bin \ - dapboot-olimexstm32h103-high-128.bin + dapboot-olimexstm32h103-high-128.bin \ + dapboot-bluepillplusstm32-high-128.bin clean: $(Q)$(RM) $(BUILD_DIR)/*.bin @@ -75,6 +78,12 @@ dapboot-olimexstm32h103.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=OLIMEXSTM32H103 -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) +dapboot-bluepillplusstm32.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32 -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + dapboot-bluepill-high.bin: | $(BUILD_DIR) @printf " BUILD $(@)\n" $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ clean @@ -99,6 +108,12 @@ dapboot-olimexstm32h103-high.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) +dapboot-bluepillplusstm32-high.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + dapboot-bluepill-high-128.bin: | $(BUILD_DIR) @printf " BUILD $(@)\n" $(Q)$(MAKE) TARGET=BLUEPILL_HIGH_128 -C src/ clean @@ -122,3 +137,9 @@ dapboot-olimexstm32h103-high-128.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH_128 -C src/ clean $(Q)$(MAKE) TARGET=OLIMEXSTM32H103_HIGH_128 -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bluepillplusstm32-high-128.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH_128 -C src/ clean + $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH_128 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) diff --git a/src/stm32f103/bluepillplus/config.h b/src/stm32f103/bluepillplus/config.h new file mode 100644 index 0000000..a50579f --- /dev/null +++ b/src/stm32f103/bluepillplus/config.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_SIZE_OVERRIDE +#define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 1024 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 0 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOB +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO2 +#endif + +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 1 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO0 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 0 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif +#ifndef USES_GPIOB +#define USES_GPIOB 1 +#endif + +#endif diff --git a/src/targets.mk b/src/targets.mk index feecb2f..c044ebf 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -100,6 +100,25 @@ ifeq ($(TARGET),OLIMEXSTM32H103_HIGH_128) ARCH = STM32F1 DEFS += -DBOOTLOADER_HIGH endif +ifeq ($(TARGET),BLUEPILLPLUSSTM32) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus + LDSCRIPT := ./stm32f103/stm32f103x8_high.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH_128) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus + LDSCRIPT := ./stm32f103/stm32f103xb_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif ifeq ($(TARGET),STM32L1_GENERIC) TARGET_COMMON_DIR := ./stm32l1 TARGET_SPEC_DIR := ./stm32l1/generic From 30aebb7bd3db187caaff32b5ec2c94f4ded59d6e Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Fri, 3 Sep 2021 16:49:01 -0700 Subject: [PATCH 38/66] A tiny refactoring of the toolchain fetch helper that makes it also work on macos --- util/install-toolchain.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/install-toolchain.sh b/util/install-toolchain.sh index 0b236df..438bc1f 100755 --- a/util/install-toolchain.sh +++ b/util/install-toolchain.sh @@ -1,7 +1,14 @@ #!/bin/bash set -eo pipefail -URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2 + +if [ `uname` == 'Darwin' ]; then +TOOLCHAIN_ARCH=mac +else +TOOLCHAIN_ARCH=x86_64-linux +fi +TOOLCHAIN_REV=9-2019q4 TOOLCHAIN=gcc-arm-none-eabi-9-2019-q4-major +URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/${TOOLCHAIN_REV}/${TOOLCHAIN}-${TOOLCHAIN_ARCH}.tar.bz2 TOOLCHAINS=$HOME/toolchains TOOLCHAIN_MISSING=0 GCC=${TOOLCHAINS}/gcc-arm-embedded/bin/arm-none-eabi-gcc From f66d7d5fea366386fc53f902f0b6f76dd02a89fc Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Fri, 3 Sep 2021 17:20:43 -0700 Subject: [PATCH 39/66] Slightly jumped the gun on the previous commit. There were other linuxisms in the script --- util/install-toolchain.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/install-toolchain.sh b/util/install-toolchain.sh index 438bc1f..bb13b45 100755 --- a/util/install-toolchain.sh +++ b/util/install-toolchain.sh @@ -27,13 +27,15 @@ if [ $TOOLCHAIN_MISSING -eq 1 ]; then ln -s $TOOLCHAIN ${TOOLCHAINS}/gcc-arm-embedded fi; -EXISTING_TOOLCHAIN=`readlink -f "${TOOLCHAINS}/gcc-arm-embedded"` +EXISTING_TOOLCHAIN=`readlink "${TOOLCHAINS}/gcc-arm-embedded"` echo "Current toolchain is $EXISTING_TOOLCHAIN" +if [ $TOOLCHAIN_ARCH != 'mac' ]; then if ! ldd ${GCC} >/dev/null; then echo "${GCC} does not appear to be executable on this machine" exit 1 fi; +fi; TOOLCHAIN_VER=`${GCC} --version | head -n 1` echo "Installed toolchain version is $TOOLCHAIN_VER" From 9b544e5c11c13cb3637bdd642f189568e5f75096 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Fri, 3 Sep 2021 17:47:41 -0700 Subject: [PATCH 40/66] Allow more flexibility when configuring from config.h In order to be able to override the VID and PID for a device from inside its config.h, config.h needs to be included before usb_conf.h --- src/dapboot.c | 2 +- src/dfu.c | 2 +- src/usb_conf.c | 1 + src/webusb.c | 2 +- src/winusb.c | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dapboot.c b/src/dapboot.c index a96c483..48d49ec 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -21,11 +21,11 @@ #include "dapboot.h" #include "target.h" +#include "config.h" #include "usb_conf.h" #include "dfu.h" #include "webusb.h" #include "winusb.h" -#include "config.h" static inline void __set_MSP(uint32_t topOfMainStack) { asm("msr msp, %0" : : "r" (topOfMainStack)); diff --git a/src/dfu.c b/src/dfu.c index 640a2a2..9ab88ce 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -25,11 +25,11 @@ #include #include "dfu.h" +#include "config.h" #include "usb_conf.h" #include "dfu_defs.h" #include "target.h" #include "dapboot.h" -#include "config.h" #ifndef TARGET_DFU_WTRANSFERSIZE #define TARGET_DFU_WTRANSFERSIZE USB_CONTROL_BUF_SIZE diff --git a/src/usb_conf.c b/src/usb_conf.c index e6095a6..27b62bf 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -25,6 +25,7 @@ #include "dfu.h" #include "webusb.h" +#include "config.h" #include "usb_conf.h" static const struct usb_device_descriptor dev = { diff --git a/src/webusb.c b/src/webusb.c index 72c37f3..8bd52b1 100644 --- a/src/webusb.c +++ b/src/webusb.c @@ -19,8 +19,8 @@ #include #include "webusb.h" -#include "usb_conf.h" #include "config.h" +#include "usb_conf.h" #ifndef LANDING_PAGE_URL #define LANDING_PAGE_URL "devanlai.github.io/webdfu/dfu-util/" diff --git a/src/winusb.c b/src/winusb.c index 26aeb0d..c5b35de 100644 --- a/src/winusb.c +++ b/src/winusb.c @@ -19,6 +19,7 @@ #include #include "winusb.h" +#include "config.h" #include "usb_conf.h" static const struct winusb_compatible_id_descriptor winusb_wcid = { From af09454045a11cd00caa06931ce4d95f665409aa Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Fri, 3 Sep 2021 18:39:20 -0700 Subject: [PATCH 41/66] Extract Vendor, Product, and Interface strings so they can be overridden with target settings --- README.md | 5 +++++ src/usb_conf.c | 10 +++------- src/usb_conf.h | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8045133..0b85546 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,11 @@ To use a custom VID/PID pair, you need to set the macros `USB_VID` and `USB_PID` DEFS="-DUSB_VID=0x1209 -DUSB_PID=0xCAFE" make +## USB Vendor, Product, and Interface strings + +To customize the USB vendor, product, and interface strings that DAPBoot reports to the USB host, you may set the macros `USB_VENDOR_STRING`, `USB_PRODUCT_STRING`, and `USB_INTERFACE_STRING`, respectively. You can do this by setting the `DEFS` environment variable or including these macros in your board's `config.h` + + ## Licensing All contents of the dapboot project are licensed under terms that are compatible with the terms of the GNU Lesser General Public License version 3. diff --git a/src/usb_conf.c b/src/usb_conf.c index e6095a6..5470c29 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -97,14 +97,10 @@ static const struct usb_bos_descriptor bos = { static char serial_number[USB_SERIAL_NUM_LENGTH+1]; static const char *usb_strings[] = { - "Devanarchy", -#ifdef BOOTLOADER_HIGH - "DAPBoot High-Memory DFU Bootloader", -#else - "DAPBoot DFU Bootloader", -#endif + USB_VENDOR_STRING, + USB_PRODUCT_STRING, serial_number, - "DAPBoot DFU" + USB_INTERFACE_STRING }; /* Buffer to be used for control requests. */ diff --git a/src/usb_conf.h b/src/usb_conf.h index 441e2dd..3972abe 100644 --- a/src/usb_conf.h +++ b/src/usb_conf.h @@ -25,10 +25,29 @@ #define USB_VID 0x1209 #endif +#ifndef USB_VENDOR_STRING +#define USB_VENDOR_STRING "Devanarchy" +#endif + #ifndef USB_PID #define USB_PID 0xdb42 #endif + +#ifndef USB_PRODUCT_STRING + +#ifdef BOOTLOADER_HIGH +#define USB_PRODUCT_STRING "DAPBoot High-Memory DFU Bootloader" +#else +#define USB_PRODUCT_STRING "DAPBoot DFU Bootloader" +#endif + +#endif + +#ifndef USB_INTERFACE_STRING +#define USB_INTERFACE_STRING "DAPBoot DFU" +#endif + #define USB_CONTROL_BUF_SIZE 1024 #define USB_SERIAL_NUM_LENGTH 24 #define INTF_DFU 0 From 4f61d96de8f3c1660aaae513fd85801ebdc4a514 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Fri, 3 Sep 2021 20:22:42 -0700 Subject: [PATCH 42/66] Add a new target for the GigaDevice variant of the BluePill Plus. WeAct ships two variants of this board, one with an STM32F103 and another with a GD32F303 series chip. The two boards are otherwise identical. The GD32F303 series is pin-compatible and register compatible with the STM32F103, but features a cortex M4, more ram, and more flash. In this case, the chip included has 48k of RAM and 256k of flash. The biggest difference is that flash pages are 2k and not 1k. While it's a little bit gross to be compiling code targeting the Cortex M3 on an ST chip and using it on an M4 made by somebody else, this was exactly the use case envisioned by GigaDevice. I've tested this bootloader on actual hardware and additionally validated that WebDFU works from https://devanlai.github.io/webdfu/dfu-util/ to it. The LED does *not* light up in the bootloader and I haven't dug into that, although the pin definition is correctly targeting PB2. --- src/stm32f103/bluepillplus-gd32/config.h | 81 ++++++++++++++++++++++++ src/targets.mk | 6 ++ 2 files changed, 87 insertions(+) create mode 100644 src/stm32f103/bluepillplus-gd32/config.h diff --git a/src/stm32f103/bluepillplus-gd32/config.h b/src/stm32f103/bluepillplus-gd32/config.h new file mode 100644 index 0000000..0b1c84d --- /dev/null +++ b/src/stm32f103/bluepillplus-gd32/config.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_SIZE_OVERRIDE +#define FLASH_SIZE_OVERRIDE 0x20000 +#endif +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 2048 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +#ifndef HAVE_LED +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 0 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOB +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO2 +#endif + +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 1 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO0 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 0 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif +#ifndef USES_GPIOB +#define USES_GPIOB 1 +#endif + +#endif diff --git a/src/targets.mk b/src/targets.mk index c044ebf..1ab46a3 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -119,6 +119,12 @@ ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH_128) ARCH = STM32F1 DEFS += -DBOOTLOADER_HIGH endif +ifeq ($(TARGET),BLUEPILLPLUSGD32) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/bluepillplus-gd32 + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif ifeq ($(TARGET),STM32L1_GENERIC) TARGET_COMMON_DIR := ./stm32l1 TARGET_SPEC_DIR := ./stm32l1/generic From 7f8279d380018174d4fcc317d66b9465e899237d Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Mon, 6 Sep 2021 22:45:43 +0300 Subject: [PATCH 43/66] Fix detach request callback being treated as manifest request callback This prevents the bootloader from detaching prematurely after download and makes it compliant with the DFU 1.1 spec. Many flashing routines do not expect an automatic detach, so I've assumed the DFU spec option to return to idle after download as what was originally intended (an automatic reset can still be achieved by passing the -R flag to dfu-util for example), in other words the bootloader was intended to have the USB_DFU_MANIFEST_TOLERANT attribute since there is no STATE_DFU_MANIFEST_WAIT_RESET in the code. --- src/dapboot.c | 2 +- src/dfu.c | 8 +++----- src/dfu.h | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/dapboot.c b/src/dapboot.c index 48d49ec..3c415fd 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -75,7 +75,7 @@ int main(void) { } usbd_device* usbd_dev = usb_setup(); - dfu_setup(usbd_dev, &target_manifest_app, NULL, NULL); + dfu_setup(usbd_dev, NULL, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); diff --git a/src/dfu.c b/src/dfu.c index 9ab88ce..eb843db 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -40,7 +40,7 @@ const struct usb_dfu_descriptor dfu_function = { .bDescriptorType = DFU_FUNCTIONAL, .bmAttributes = ((DFU_DOWNLOAD_AVAILABLE ? USB_DFU_CAN_DOWNLOAD : 0) | (DFU_UPLOAD_AVAILABLE ? USB_DFU_CAN_UPLOAD : 0) | - USB_DFU_WILL_DETACH ), + USB_DFU_MANIFEST_TOLERANT), .wDetachTimeout = 255, .wTransferSize = TARGET_DFU_WTRANSFERSIZE, .bcdDFUVersion = 0x0110, @@ -106,7 +106,6 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data (void)usbd_dev; (void)req; - if (DFU_PATCH_VECTORS && current_dfu_offset == 0) { if (dfu_download_size < offsetof(vector_table_t, reserved_x001c[1])) { /* Can't handle splitting the vector table right now */ @@ -144,10 +143,9 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; + if (dfu_manifest_request_callback) { dfu_manifest_request_callback(); - } else { - dfu_set_status(DFU_STATUS_ERR_UNKNOWN); } } @@ -183,7 +181,7 @@ dfu_control_class_request(usbd_device *usbd_dev, } case STATE_DFU_MANIFEST_SYNC: { if (validate_application()) { - dfu_set_state(STATE_DFU_MANIFEST); + dfu_set_state(STATE_DFU_IDLE); *complete = &dfu_on_manifest_request; } else { dfu_set_status(DFU_STATUS_ERR_FIRMWARE); diff --git a/src/dfu.h b/src/dfu.h index 9d53520..30e9dbe 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -29,7 +29,7 @@ typedef void (*StateChangeCallback)(enum dfu_state); typedef void (*StatusChangeCallback)(enum dfu_status); extern void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_detach_request, + GenericCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change); From aea139a1004d30ebda94f6da78329baf9224a572 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Tue, 7 Sep 2021 00:19:53 +0300 Subject: [PATCH 44/66] Fix RTC backup register indexing, add target-specific register and boot command According to ST's RM0008 STM32F103xx reference manual section 6.4.5 table 17 the backup registers are 32 bits wide with only the lower 16 bits being used for storing values. In addition to this, the "first" register slot is entirely reserved and there is no backup register zero, meaning that BKP_DR1 starts from an offset of 0x04. This was mostly correctly reflected in backup.c, but the 2-multiplier for the register enum throws everything off by a lot, which results in the boot command check failing to work correctly when using anything but the first backup register. BKP0 has also been removed since as described by RM0008 indexing starts at one (BKP1 is now fully equivalent to the old BKP0). I've also gone ahead and implemented support for specifying the backup register and boot command to use per-target. If not defined, options equivalent to the old behavior are used to not break existing applications. --- src/stm32f103/backup.c | 8 ++++---- src/stm32f103/backup.h | 7 ++++++- src/stm32f103/target_stm32f103.c | 12 +++++++++--- src/stm32l1/target_stm32l1.c | 13 ++++++++++--- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/stm32f103/backup.c b/src/stm32f103/backup.c index 18a4c20..c120ea7 100644 --- a/src/stm32f103/backup.c +++ b/src/stm32f103/backup.c @@ -29,13 +29,13 @@ void backup_write(enum BackupRegister reg, uint32_t value) { rcc_periph_clock_enable(RCC_BKP); pwr_disable_backup_domain_write_protect(); - RTC_BKP_DR((int)reg*2) = value & 0xFFFFUL; - RTC_BKP_DR((int)reg*2+1) = (value & 0xFFFF0000UL) >> 16; + RTC_BKP_DR((int)reg) = value & 0xFFFFUL; + RTC_BKP_DR((int)reg+1) = (value & 0xFFFF0000UL) >> 16; pwr_enable_backup_domain_write_protect(); } uint32_t backup_read(enum BackupRegister reg) { - uint32_t value = ((uint32_t)RTC_BKP_DR((int)reg*2+1) << 16) - | ((uint32_t)RTC_BKP_DR((int)reg*2) << 0); + uint32_t value = (uint32_t)RTC_BKP_DR((int)reg) + | ((uint32_t)RTC_BKP_DR((int)reg+1) << 16); return value; } diff --git a/src/stm32f103/backup.h b/src/stm32f103/backup.h index c2fe2b0..739dced 100644 --- a/src/stm32f103/backup.h +++ b/src/stm32f103/backup.h @@ -20,11 +20,16 @@ #define BACKUP_H_INCLUDED enum BackupRegister { - BKP0 = 0, BKP1, BKP2, BKP3, BKP4, + BKP5, + BKP6, + BKP7, + BKP8, + BKP9, + BKP10, }; extern void backup_write(enum BackupRegister reg, uint32_t value); diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 6948482..ba924b5 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -55,7 +55,13 @@ _Static_assert((FLASH_BASE + FLASH_SIZE_OVERRIDE >= APP_BASE_ADDRESS), "Incompatible flash size"); #endif -static const uint32_t CMD_BOOT = 0x544F4F42UL; +#ifndef REG_BOOT +#define REG_BOOT BKP1 +#endif + +#ifndef CMD_BOOT +#define CMD_BOOT 0x544F4F42UL +#endif void target_clock_setup(void) { #ifdef USE_HSI @@ -160,13 +166,13 @@ const usbd_driver* target_usb_init(void) { bool target_get_force_bootloader(void) { bool force = false; /* Check the RTC backup register */ - uint32_t cmd = backup_read(BKP0); + uint32_t cmd = backup_read(REG_BOOT); if (cmd == CMD_BOOT) { force = true; } /* Clear the RTC backup register */ - backup_write(BKP0, 0); + backup_write(REG_BOOT, 0); #if HAVE_BUTTON /* Wait some time in case the button has some debounce capacitor */ diff --git a/src/stm32l1/target_stm32l1.c b/src/stm32l1/target_stm32l1.c index 57b4825..f94bd90 100644 --- a/src/stm32l1/target_stm32l1.c +++ b/src/stm32l1/target_stm32l1.c @@ -27,8 +27,15 @@ #include "config.h" #include "backup.h" +#ifndef REG_BOOT +#define REG_BOOT BKP1 +#endif + +#ifndef CMD_BOOT +#define CMD_BOOT 0x544F4F42UL +#endif + //#define CMD_FAST_BOOT 0xfa57b007 -static const uint32_t CMD_BOOT = 0x544F4F42UL; void target_clock_setup(void) { @@ -82,11 +89,11 @@ const usbd_driver* target_usb_init(void) bool target_get_force_bootloader(void) { bool enter_bl = false; - uint32_t cmd = backup_read(BKP0); + uint32_t cmd = backup_read(REG_BOOT); if (cmd == CMD_BOOT) { enter_bl = true; } - backup_write(BKP0, 0); + backup_write(REG_BOOT, 0); #if HAVE_BUTTON #warning HAVE_BUTTON not implemented for L1 From 5aa7684e966b7e955e6cba4834600f42620ac5b0 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Tue, 7 Sep 2021 01:24:10 +0300 Subject: [PATCH 45/66] Enable specifying the alternate setting for the DFU interface per-target This works by "padding" the DFU alternate setting with dummy no-op descriptors that occupy all previous alternate setting values. AFAICT while these no-op interfaces are not documented in the USB defined class codes, they don't identify as anything in Linux and cause no driver to be loaded nor any warnings in the system logs, and they should be fine for other operating systems as well. When USB_ALT > 0, the DFU interface appears in the specific alternate setting, which dfu-util does automatically identify and use, alternatively it can be manually targeted with the -a flag. To have USB_ALT be a per-target (optional) property the target.h implementor must load it (since it has the macro value to keep everything compile-time), which is why I've had to split out the descriptor structs into their own file to be preprocessed as part of target_stm32f103.c or target_stm32l1.c. The USB initialization code can then call target_usb_descriptor() to get the pointer to the configuration descriptor defined by the target. --- src/stm32f103/target_stm32f103.c | 5 ++ src/stm32l1/target_stm32l1.c | 6 ++ src/target.h | 1 + src/usb_conf.c | 40 +----------- src/usb_descriptor.h | 102 +++++++++++++++++++++++++++++++ 5 files changed, 115 insertions(+), 39 deletions(-) create mode 100644 src/usb_descriptor.h diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 6948482..d7d02c2 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -29,6 +29,7 @@ #include "target.h" #include "config.h" #include "backup.h" +#include "usb_descriptor.h" #ifndef USES_GPIOA #if (HAVE_USB_PULLUP_CONTROL == 0) @@ -157,6 +158,10 @@ const usbd_driver* target_usb_init(void) { return &st_usbfs_v1_usb_driver; } +const struct usb_config_descriptor* target_usb_descriptor(void) { + return &usb_config; +} + bool target_get_force_bootloader(void) { bool force = false; /* Check the RTC backup register */ diff --git a/src/stm32l1/target_stm32l1.c b/src/stm32l1/target_stm32l1.c index 57b4825..5371e62 100644 --- a/src/stm32l1/target_stm32l1.c +++ b/src/stm32l1/target_stm32l1.c @@ -26,6 +26,7 @@ #include "target.h" #include "config.h" #include "backup.h" +#include "usb_descriptor.h" //#define CMD_FAST_BOOT 0xfa57b007 static const uint32_t CMD_BOOT = 0x544F4F42UL; @@ -75,6 +76,11 @@ const usbd_driver* target_usb_init(void) return &st_usbfs_v1_usb_driver; } +const struct usb_config_descriptor* target_usb_descriptor(void) +{ + return &usb_config; +} + /* This implementation will always start in bootloader, unless the app * has asked it to skipp straight forwards * You may wish to fill in button handling... diff --git a/src/target.h b/src/target.h index 7e671cf..92fb7d0 100644 --- a/src/target.h +++ b/src/target.h @@ -26,6 +26,7 @@ extern void target_clock_setup(void); extern void target_gpio_setup(void); extern const usbd_driver* target_usb_init(void); +extern const struct usb_config_descriptor* target_usb_descriptor(void); extern bool target_get_force_bootloader(void); extern void target_get_serial_number(char* dest, size_t max_chars); extern size_t target_get_max_firmware_size(void); diff --git a/src/usb_conf.c b/src/usb_conf.c index dc88e6a..e81de03 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -45,44 +45,6 @@ static const struct usb_device_descriptor dev = { .bNumConfigurations = 1, }; -static const struct usb_interface_descriptor dfu_iface = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = INTF_DFU, - .bAlternateSetting = 0, - .bNumEndpoints = 0, - .bInterfaceClass = 0xFE, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 2, - .iInterface = 4, - - .endpoint = NULL, - - .extra = &dfu_function, - .extralen = sizeof(dfu_function), -}; - -static const struct usb_interface interfaces[] = { - /* DFU interface */ - { - .num_altsetting = 1, - .altsetting = &dfu_iface, - } -}; - -static const struct usb_config_descriptor config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = sizeof(interfaces)/sizeof(struct usb_interface), - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0xC0, - .bMaxPower = 0x32, - - .interface = interfaces, -}; - static const struct usb_device_capability_descriptor* capabilities[] = { (const struct usb_device_capability_descriptor*)&webusb_platform, }; @@ -119,7 +81,7 @@ usbd_device* usb_setup(void) { int num_strings = sizeof(usb_strings)/sizeof(const char*); const usbd_driver* driver = target_usb_init(); - usbd_device* usbd_dev = usbd_init(driver, &dev, &config, &bos, + usbd_device* usbd_dev = usbd_init(driver, &dev, target_usb_descriptor(), &bos, usb_strings, num_strings, usbd_control_buffer, sizeof(usbd_control_buffer)); diff --git a/src/usb_descriptor.h b/src/usb_descriptor.h new file mode 100644 index 0000000..40e11dd --- /dev/null +++ b/src/usb_descriptor.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021, Dennis Marttinen + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DAPBOOT_USB_DESCRIPTOR_H +#define DAPBOOT_USB_DESCRIPTOR_H + +#include "usb_conf.h" +#include "dfu.h" + +// Macro to create a dummy (no-op) USB interface descriptor with the given alternate setting +#define ALT_DUMMY(N) { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = INTF_DFU, \ + .bAlternateSetting = (N), \ + .bNumEndpoints = 0, \ + .bInterfaceClass = 0, \ + .bInterfaceSubClass = 0, \ + .bInterfaceProtocol = 0, \ + .iInterface = 0, \ + .endpoint = NULL, \ + .extra = NULL, \ + .extralen = 0, \ +}, + +// Functionality for creating repetitive ALT_DUMMY structs with an increasing count during compile time. +// It doesn't look very nice, but C doesn't allow loops in preprocessor macros, so this needs to be hard-coded. +#define ALT0 +#define ALT1 ALT_DUMMY(0) +#define ALT2 ALT1 ALT_DUMMY(1) +#define ALT3 ALT2 ALT_DUMMY(2) +#define ALT4 ALT3 ALT_DUMMY(3) +#define ALT5 ALT4 ALT_DUMMY(4) +#define ALTC(n) ALT##n +#define ALTN(n) ALTC(n) + +// The DFU interface doesn't have any special alternate setting by default +#ifndef USB_ALT +#define USB_ALT 0 +#endif + +static const struct usb_interface_descriptor altsettings[] = { + ALTN(USB_ALT) // Prepend USB_ALT dummy USB interface descriptors to "pad" the real one + { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = INTF_DFU, + .bAlternateSetting = USB_ALT, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 2, + .iInterface = 0, + + .endpoint = NULL, + + .extra = &dfu_function, + .extralen = sizeof(dfu_function), + } +}; + +// Tracking this is mandatory if exposing multiple altsettings +static uint8_t cur_altsetting = 0; + +static const struct usb_interface interfaces[] = { + /* DFU interface */ + { + .cur_altsetting = &cur_altsetting, + .num_altsetting = USB_ALT + 1, + .altsetting = (const struct usb_interface_descriptor*)&altsettings, + }, +}; + +static const struct usb_config_descriptor usb_config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = sizeof(interfaces)/sizeof(struct usb_interface), + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0xC0, + .bMaxPower = 0x32, + + .interface = interfaces, +}; + +#endif //DAPBOOT_USB_DESCRIPTOR_H From e26b4741ab0653f2d696d5135cd226b1804547f6 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Tue, 7 Sep 2021 02:12:11 +0300 Subject: [PATCH 46/66] Fix: set BOOTLOADER_HIGH for target BLUEPILLPLUSSTM32_HIGH --- src/targets.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/src/targets.mk b/src/targets.mk index c044ebf..ee3a3cd 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -111,6 +111,7 @@ ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH) TARGET_SPEC_DIR := ./stm32f103/bluepillplus LDSCRIPT := ./stm32f103/stm32f103x8_high.ld ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH endif ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH_128) TARGET_COMMON_DIR := ./stm32f103 From d7854959b44cecc3e5591e91dbcc6c7cf5108a9c Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Tue, 7 Sep 2021 02:11:08 +0300 Subject: [PATCH 47/66] Add support for the BigTreeTech SKR MINI E3 V2.0 board and STM32F103RC The BigTreeTech SKR MINI E3 V2.0 is a 3D printer motherboard based on the STM32F103RC with 256 KiB of flash. Out of the box it has a MOSFET controlling the USB pullup wired to the SWCLK pin, which makes attaching a debugger very difficult. The USBMOD target variant has pullup control disabled for people that feel adventurous enough to desolder some components from their board to fix this issue, there is a more in-depth explanation in the USBMOD config.h. Both targets have a high memory variant which has been tested and confirmed working with the Klipper firmware by specifying "no bootloader" in its memory offset configuration menu. --- README.md | 5 +- src/stm32f103/skrminie3v2/config.h | 96 ++++++++++++++++++++ src/stm32f103/skrminie3v2_usbmod/config.h | 103 ++++++++++++++++++++++ src/stm32f103/stm32f103x8.ld | 4 +- src/stm32f103/stm32f103xc_high.ld | 31 +++++++ src/targets.mk | 26 ++++++ 6 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 src/stm32f103/skrminie3v2/config.h create mode 100644 src/stm32f103/skrminie3v2_usbmod/config.h create mode 100644 src/stm32f103/stm32f103xc_high.ld diff --git a/README.md b/README.md index 0b85546..5cecc3e 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,17 @@ To build other targets, you can override the |`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones | |`OLIMEXSTM32H103` | Olimex STM32-H103 | https://www.olimex.com/Products/ARM/ST/STM32-H103/ | |`BLUEPILLPLUSSTM32` | Bluepill with USB C | https://github.com/WeActTC/BluePill-Plus/ | +|`BTTSKRMINIE3V2` | BigTreeTech SKR MINI E3 V2.0 (3D printer motherboard) | https://github.com/bigtreetech/BIGTREETECH-SKR-mini-E3 | +|`BTTSKRMINIE3V2_USBMOD` | BTT SKR MINI E3 V2.0 with USB pullup removed | https://github.com/bigtreetech/BIGTREETECH-SKR-mini-E3 | -For each of the above targets, there are three variants that can be added to the target name: +For the above targets there are some potential variants that can be added to the target name based on what the target supports: | Target Variant | Description | | -------------- | ----------------------------------------------------- | |` ` | Standard bootloader, using first 8kB of flash | |`_HIGH` | High memory bootloader for 64kB chips (experimental) | |`_HIGH_128` | High memory bootloader for 128kB chips (experimental) | +|`_HIGH_256` | High memory bootloader for 256kB chips (experimental) | The high memory bootloader is a variation that doesn't require the application to be at an offset, the bootloader resides in the top 6.5kB of ROM and hides its reset and stack vectors inside unused entries of the application vector table. As an example, to compile for a Bluepill board with 128kB flash, use: diff --git a/src/stm32f103/skrminie3v2/config.h b/src/stm32f103/skrminie3v2/config.h new file mode 100644 index 0000000..39fba7d --- /dev/null +++ b/src/stm32f103/skrminie3v2/config.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2021, Dennis Marttinen + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_PAGE_SIZE +/* The BTT SKR MINI E3 V2.0 uses an STM32F103RC with 256 KiB of flash and 2 KiB pages */ +#define FLASH_PAGE_SIZE 2048 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +/* There is a PCB-mounted status LED, but it's unreadable when the board is installed, + * and additionally it's wired to SWDIO so toggling it manually is a bad idea */ +#ifndef HAVE_LED +#define HAVE_LED 0 +#endif + +/* Display encoder button (BTN-ENC) on PA15, no external pullup */ +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 0 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO15 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 1 +#endif +#ifndef USB_PULLUP_GPIO_PORT +#define USB_PULLUP_GPIO_PORT GPIOA +#endif +#ifndef USB_PULLUP_GPIO_PIN +#define USB_PULLUP_GPIO_PIN GPIO14 +#endif +#ifndef USB_PULLUP_ACTIVE_HIGH +#define USB_PULLUP_ACTIVE_HIGH 0 +#endif +#ifndef USB_PULLUP_OPEN_DRAIN +#define USB_PULLUP_OPEN_DRAIN 1 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif + +/* For stm32duino bootloader compatibility, the following options enable + * bootloader flashing using KIAUH: https://github.com/th33xitus/kiauh */ +#ifndef REG_BOOT +#define REG_BOOT BKP10 +#endif + +#ifndef CMD_BOOT +#define CMD_BOOT 1 +#endif + +#ifndef USB_ALT +#define USB_ALT 2 +#endif + +#endif diff --git a/src/stm32f103/skrminie3v2_usbmod/config.h b/src/stm32f103/skrminie3v2_usbmod/config.h new file mode 100644 index 0000000..089a6f3 --- /dev/null +++ b/src/stm32f103/skrminie3v2_usbmod/config.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021, Dennis Marttinen + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * ATTENTION! Using the USBMOD target requires hardware modifications to your BTT SKR MINI E3 V2.0. These modifications + * aim to restore the broken SWD debugging support of this board by removing "unnecessary" components attached to the + * SWDIO and SWDCLK pins. Read the comments carefully, and proceed at your own risk! Flashing this firmware on an + * unmodified SKR MINI will not harm it, but USB will not work in the bootloader. Here's the schematic for the board for + * looking up the component identifiers mentioned in the comments: + * https://github.com/bigtreetech/BIGTREETECH-SKR-mini-E3/blob/master/hardware/BTT%20SKR%20MINI%20E3%20V2.0/Hardware/BTT%20SKR%20MINI%20E3%20V2.0SCHpdf.PDF + */ + +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifndef APP_BASE_ADDRESS +#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) +#endif +#ifndef FLASH_PAGE_SIZE +/* The BTT SKR MINI E3 V2.0 uses an STM32F103RC with 256 KiB of flash and 2 KiB pages */ +#define FLASH_PAGE_SIZE 2048 +#endif +#ifndef DFU_UPLOAD_AVAILABLE +#define DFU_UPLOAD_AVAILABLE 1 +#endif +#ifndef DFU_DOWNLOAD_AVAILABLE +#define DFU_DOWNLOAD_AVAILABLE 1 +#endif + +/* There is a PCB-mounted status LED, but it's unreadable when the board is installed, and additionally it's wired to + * SWDIO so toggling it manually is a bad idea. To restore reliable SWD communication, desolder either the status LED + * itself (D16) or the resistor for it (R90). */ +#ifndef HAVE_LED +#define HAVE_LED 0 +#endif + +/* Display encoder button (BTN-ENC) on PA15, no external pullup */ +#ifndef HAVE_BUTTON +#define HAVE_BUTTON 1 +#endif +#ifndef BUTTON_ACTIVE_HIGH +#define BUTTON_ACTIVE_HIGH 0 +#endif +#ifndef BUTTON_GPIO_PORT +#define BUTTON_GPIO_PORT GPIOA +#endif +#ifndef BUTTON_GPIO_PIN +#define BUTTON_GPIO_PIN GPIO15 +#endif +#ifndef BUTTON_USES_PULL +#define BUTTON_USES_PULL 1 +#endif +#ifndef BUTTON_SAMPLE_DELAY_CYCLES +#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 +#endif + +/* The USB pullup MOSFET (U7) with its pullup resistor (R43) is wired to SWCLK on this board which makes attaching a + * debug probe nearly impossible. It is however possible to desolder these two components and bridge the source and + * drain contact pads for the MOSFET to gain debugger support. The [datasheet] for the MCU suggests that the pullup + * resistor for the USB D+ pin (R44) is enough on its own, as the MCU itself can pull the pin down to initiate a USB + * reset without an external MOSFET controlling that resistor. This does indeed work without any configuration in + * dapboot as well as both the Klipper and Marlin 3D printer firmwares. There were no problems during testing when + * performing software resets or using the reset button, the USB reset works seamlessly even when transitioning from + * dapboot to a freshly flashed firmware. + * [datasheet]: https://www.st.com/resource/en/datasheet/stm32f103rc.pdf */ +#ifndef HAVE_USB_PULLUP_CONTROL +#define HAVE_USB_PULLUP_CONTROL 0 +#endif + +#ifndef USES_GPIOA +#define USES_GPIOA 1 +#endif + +/* For stm32duino bootloader compatibility, the following options enable + * bootloader flashing using KIAUH: https://github.com/th33xitus/kiauh */ +#ifndef REG_BOOT +#define REG_BOOT BKP10 +#endif + +#ifndef CMD_BOOT +#define CMD_BOOT 1 +#endif + +#ifndef USB_ALT +#define USB_ALT 2 +#endif + +#endif diff --git a/src/stm32f103/stm32f103x8.ld b/src/stm32f103/stm32f103x8.ld index 507e5ea..5bf2870 100644 --- a/src/stm32f103/stm32f103x8.ld +++ b/src/stm32f103/stm32f103x8.ld @@ -18,8 +18,8 @@ */ /* Linker script for STM32F103x8, 64k flash, 20k RAM. - * This script also works for the STM32F103xB, as the bootloader uses only the - * first 8kB of flash. */ + * This script also works for the STM32F103xB and the STM32F103xC, + * as the bootloader only uses the first 8kB of flash. */ /* Define memory regions. */ MEMORY diff --git a/src/stm32f103/stm32f103xc_high.ld b/src/stm32f103/stm32f103xc_high.ld new file mode 100644 index 0000000..7ca33f4 --- /dev/null +++ b/src/stm32f103/stm32f103xc_high.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2015 Karl Palsson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for STM32F103xC, 256k flash, 48k RAM. */ + +/* Define memory regions. */ +MEMORY +{ + vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 + rom (rx) : ORIGIN = 0x0803e600, LENGTH = 0x1A00 + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 48K +} + +/* Include the common ld script. */ +INCLUDE stm32f103/stm32f1.ld diff --git a/src/targets.mk b/src/targets.mk index ee3a3cd..0b0147c 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -120,6 +120,32 @@ ifeq ($(TARGET),BLUEPILLPLUSSTM32_HIGH_128) ARCH = STM32F1 DEFS += -DBOOTLOADER_HIGH endif +ifeq ($(TARGET),BTTSKRMINIE3V2) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/skrminie3v2 + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),BTTSKRMINIE3V2_HIGH_256) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/skrminie3v2 + LDSCRIPT := ./stm32f103/stm32f103xc_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif +ifeq ($(TARGET),BTTSKRMINIE3V2_USBMOD) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/skrminie3v2_usbmod + LDSCRIPT := ./stm32f103/stm32f103x8.ld + ARCH = STM32F1 +endif +ifeq ($(TARGET),BTTSKRMINIE3V2_USBMOD_HIGH_256) + TARGET_COMMON_DIR := ./stm32f103 + TARGET_SPEC_DIR := ./stm32f103/skrminie3v2_usbmod + LDSCRIPT := ./stm32f103/stm32f103xc_high.ld + ARCH = STM32F1 + DEFS += -DBOOTLOADER_HIGH +endif ifeq ($(TARGET),STM32L1_GENERIC) TARGET_COMMON_DIR := ./stm32l1 TARGET_SPEC_DIR := ./stm32l1/generic From e49cd53629a55729d2b560907bb9ede1f52fc740 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Tue, 7 Sep 2021 02:30:21 +0300 Subject: [PATCH 48/66] Add BTT SKR MINI E3 V2.0 targets to release.Makefile --- release.Makefile | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/release.Makefile b/release.Makefile index b217af0..34a29c1 100644 --- a/release.Makefile +++ b/release.Makefile @@ -34,6 +34,8 @@ all: dapboot-bluepill.bin \ dapboot-stlink.bin \ dapboot-olimexstm32h103.bin \ dapboot-bluepillplusstm32.bin \ + dapboot-bttskrminie3v2.bin \ + dapboot-bttskrminie3v2-usbmod.bin \ dapboot-bluepill-high.bin \ dapboot-maplemini-high.bin \ dapboot-stlink-high.bin \ @@ -43,7 +45,9 @@ all: dapboot-bluepill.bin \ dapboot-maplemini-high-128.bin \ dapboot-stlink-high-128.bin \ dapboot-olimexstm32h103-high-128.bin \ - dapboot-bluepillplusstm32-high-128.bin + dapboot-bluepillplusstm32-high-128.bin \ + dapboot-bttskrminie3v2-high-256.bin \ + dapboot-bttskrminie3v2-usbmod-high-256.bin clean: $(Q)$(RM) $(BUILD_DIR)/*.bin @@ -84,6 +88,18 @@ dapboot-bluepillplusstm32.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32 -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) +dapboot-bttskrminie3v2.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2 -C src/ clean + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bttskrminie3v2-usbmod.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD -C src/ clean + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + dapboot-bluepill-high.bin: | $(BUILD_DIR) @printf " BUILD $(@)\n" $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ clean @@ -143,3 +159,15 @@ dapboot-bluepillplusstm32-high-128.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH_128 -C src/ clean $(Q)$(MAKE) TARGET=BLUEPILLPLUSSTM32_HIGH_128 -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bttskrminie3v2-high-256.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_HIGH_256 -C src/ clean + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_HIGH_256 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) + +dapboot-bttskrminie3v2-usbmod-high-256.bin: | $(BUILD_DIR) + @printf " BUILD $(@)\n" + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD_HIGH_256 -C src/ clean + $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD_HIGH_256 -C src/ + $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) From 5ad3db6c10d079ebf193b136e65bdc20bbedf4e3 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Fri, 10 Sep 2021 22:00:43 +0300 Subject: [PATCH 49/66] Move descriptors back to usb_conf.c, set iInterface to 4, rename macro to USB_DFU_ALTN Assesses review feedback. I could initially not get the macros to work in usb_conf.c, but they seem to work now even though the changes from pull #38 have been present in the development tree from the start. --- src/stm32f103/target_stm32f103.c | 5 -- src/stm32l1/target_stm32l1.c | 6 -- src/target.h | 1 - src/usb_conf.c | 74 +++++++++++++++++++++- src/usb_conf.h | 4 ++ src/usb_descriptor.h | 102 ------------------------------- 6 files changed, 77 insertions(+), 115 deletions(-) delete mode 100644 src/usb_descriptor.h diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index d7d02c2..6948482 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -29,7 +29,6 @@ #include "target.h" #include "config.h" #include "backup.h" -#include "usb_descriptor.h" #ifndef USES_GPIOA #if (HAVE_USB_PULLUP_CONTROL == 0) @@ -158,10 +157,6 @@ const usbd_driver* target_usb_init(void) { return &st_usbfs_v1_usb_driver; } -const struct usb_config_descriptor* target_usb_descriptor(void) { - return &usb_config; -} - bool target_get_force_bootloader(void) { bool force = false; /* Check the RTC backup register */ diff --git a/src/stm32l1/target_stm32l1.c b/src/stm32l1/target_stm32l1.c index 5371e62..57b4825 100644 --- a/src/stm32l1/target_stm32l1.c +++ b/src/stm32l1/target_stm32l1.c @@ -26,7 +26,6 @@ #include "target.h" #include "config.h" #include "backup.h" -#include "usb_descriptor.h" //#define CMD_FAST_BOOT 0xfa57b007 static const uint32_t CMD_BOOT = 0x544F4F42UL; @@ -76,11 +75,6 @@ const usbd_driver* target_usb_init(void) return &st_usbfs_v1_usb_driver; } -const struct usb_config_descriptor* target_usb_descriptor(void) -{ - return &usb_config; -} - /* This implementation will always start in bootloader, unless the app * has asked it to skipp straight forwards * You may wish to fill in button handling... diff --git a/src/target.h b/src/target.h index 92fb7d0..7e671cf 100644 --- a/src/target.h +++ b/src/target.h @@ -26,7 +26,6 @@ extern void target_clock_setup(void); extern void target_gpio_setup(void); extern const usbd_driver* target_usb_init(void); -extern const struct usb_config_descriptor* target_usb_descriptor(void); extern bool target_get_force_bootloader(void); extern void target_get_serial_number(char* dest, size_t max_chars); extern size_t target_get_max_firmware_size(void); diff --git a/src/usb_conf.c b/src/usb_conf.c index e81de03..f5d3d62 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -45,6 +45,78 @@ static const struct usb_device_descriptor dev = { .bNumConfigurations = 1, }; +// Macro to create a dummy (no-op) USB interface descriptor with the given alternate setting +#define ALT_DUMMY(N) { \ + .bLength = USB_DT_INTERFACE_SIZE, \ + .bDescriptorType = USB_DT_INTERFACE, \ + .bInterfaceNumber = INTF_DFU, \ + .bAlternateSetting = (N), \ + .bNumEndpoints = 0, \ + .bInterfaceClass = 0, \ + .bInterfaceSubClass = 0, \ + .bInterfaceProtocol = 0, \ + .iInterface = 0, \ + .endpoint = NULL, \ + .extra = NULL, \ + .extralen = 0, \ +}, + +// Functionality for creating repetitive ALT_DUMMY structs with an increasing count during compile time. +// It doesn't look very nice, but C doesn't allow loops in preprocessor macros, so this needs to be hard-coded. +#define ALT0 +#define ALT1 ALT_DUMMY(0) +#define ALT2 ALT1 ALT_DUMMY(1) +#define ALT3 ALT2 ALT_DUMMY(2) +#define ALT4 ALT3 ALT_DUMMY(3) +#define ALT5 ALT4 ALT_DUMMY(4) +#define ALTW(n) ALT##n // Wrapper macro for expansion +#define ALTN(n) ALTW(n) + +static const struct usb_interface_descriptor altsettings[] = { + ALTN(USB_DFU_ALTN) // Prepend USB_DFU_ALTN dummy USB interface descriptors to "pad" the real one + { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = INTF_DFU, + .bAlternateSetting = USB_DFU_ALTN, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 2, + .iInterface = 4, + + .endpoint = NULL, + + .extra = &dfu_function, + .extralen = sizeof(dfu_function), + } +}; + +// Tracking this is mandatory if exposing multiple altsettings +static uint8_t cur_altsetting = 0; + +static const struct usb_interface interfaces[] = { + /* DFU interface */ + { + .cur_altsetting = &cur_altsetting, + .num_altsetting = USB_DFU_ALTN + 1, + .altsetting = (const struct usb_interface_descriptor*)&altsettings, + } +}; + +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = sizeof(interfaces)/sizeof(struct usb_interface), + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0xC0, + .bMaxPower = 0x32, + + .interface = interfaces, +}; + static const struct usb_device_capability_descriptor* capabilities[] = { (const struct usb_device_capability_descriptor*)&webusb_platform, }; @@ -81,7 +153,7 @@ usbd_device* usb_setup(void) { int num_strings = sizeof(usb_strings)/sizeof(const char*); const usbd_driver* driver = target_usb_init(); - usbd_device* usbd_dev = usbd_init(driver, &dev, target_usb_descriptor(), &bos, + usbd_device* usbd_dev = usbd_init(driver, &dev, &config, &bos, usb_strings, num_strings, usbd_control_buffer, sizeof(usbd_control_buffer)); diff --git a/src/usb_conf.h b/src/usb_conf.h index 3972abe..e773e5b 100644 --- a/src/usb_conf.h +++ b/src/usb_conf.h @@ -33,6 +33,10 @@ #define USB_PID 0xdb42 #endif +// The DFU interface doesn't have any special alternate setting by default +#ifndef USB_DFU_ALTN +#define USB_DFU_ALTN 0 +#endif #ifndef USB_PRODUCT_STRING diff --git a/src/usb_descriptor.h b/src/usb_descriptor.h deleted file mode 100644 index 40e11dd..0000000 --- a/src/usb_descriptor.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2021, Dennis Marttinen - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice - * appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef DAPBOOT_USB_DESCRIPTOR_H -#define DAPBOOT_USB_DESCRIPTOR_H - -#include "usb_conf.h" -#include "dfu.h" - -// Macro to create a dummy (no-op) USB interface descriptor with the given alternate setting -#define ALT_DUMMY(N) { \ - .bLength = USB_DT_INTERFACE_SIZE, \ - .bDescriptorType = USB_DT_INTERFACE, \ - .bInterfaceNumber = INTF_DFU, \ - .bAlternateSetting = (N), \ - .bNumEndpoints = 0, \ - .bInterfaceClass = 0, \ - .bInterfaceSubClass = 0, \ - .bInterfaceProtocol = 0, \ - .iInterface = 0, \ - .endpoint = NULL, \ - .extra = NULL, \ - .extralen = 0, \ -}, - -// Functionality for creating repetitive ALT_DUMMY structs with an increasing count during compile time. -// It doesn't look very nice, but C doesn't allow loops in preprocessor macros, so this needs to be hard-coded. -#define ALT0 -#define ALT1 ALT_DUMMY(0) -#define ALT2 ALT1 ALT_DUMMY(1) -#define ALT3 ALT2 ALT_DUMMY(2) -#define ALT4 ALT3 ALT_DUMMY(3) -#define ALT5 ALT4 ALT_DUMMY(4) -#define ALTC(n) ALT##n -#define ALTN(n) ALTC(n) - -// The DFU interface doesn't have any special alternate setting by default -#ifndef USB_ALT -#define USB_ALT 0 -#endif - -static const struct usb_interface_descriptor altsettings[] = { - ALTN(USB_ALT) // Prepend USB_ALT dummy USB interface descriptors to "pad" the real one - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = INTF_DFU, - .bAlternateSetting = USB_ALT, - .bNumEndpoints = 0, - .bInterfaceClass = 0xFE, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 2, - .iInterface = 0, - - .endpoint = NULL, - - .extra = &dfu_function, - .extralen = sizeof(dfu_function), - } -}; - -// Tracking this is mandatory if exposing multiple altsettings -static uint8_t cur_altsetting = 0; - -static const struct usb_interface interfaces[] = { - /* DFU interface */ - { - .cur_altsetting = &cur_altsetting, - .num_altsetting = USB_ALT + 1, - .altsetting = (const struct usb_interface_descriptor*)&altsettings, - }, -}; - -static const struct usb_config_descriptor usb_config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = sizeof(interfaces)/sizeof(struct usb_interface), - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0xC0, - .bMaxPower = 0x32, - - .interface = interfaces, -}; - -#endif //DAPBOOT_USB_DESCRIPTOR_H From 30e54d0322672f655d2b62fe25debc6755000d2d Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Fri, 10 Sep 2021 22:47:58 +0300 Subject: [PATCH 50/66] Use only one backup register for CMD_BOOT for stm32f103 based targets This is behavior is more akin to what most STM32F103xx applications wanting to enter the bootloader are accustomed to. Should retain backwards compatibility with existing applications writing to both registers, the higher one just won't be checked by the bootloader anymore. --- src/stm32f103/backup.c | 11 ++++------- src/stm32f103/backup.h | 4 ++-- src/stm32f103/target_stm32f103.c | 4 ++-- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/stm32f103/backup.c b/src/stm32f103/backup.c index c120ea7..1137568 100644 --- a/src/stm32f103/backup.c +++ b/src/stm32f103/backup.c @@ -24,18 +24,15 @@ #define RTC_BKP_DR(reg) MMIO16(BACKUP_REGS_BASE + 4 + (4 * (reg))) -void backup_write(enum BackupRegister reg, uint32_t value) { +void backup_write(enum BackupRegister reg, uint16_t value) { rcc_periph_clock_enable(RCC_PWR); rcc_periph_clock_enable(RCC_BKP); pwr_disable_backup_domain_write_protect(); - RTC_BKP_DR((int)reg) = value & 0xFFFFUL; - RTC_BKP_DR((int)reg+1) = (value & 0xFFFF0000UL) >> 16; + RTC_BKP_DR((int)reg) = value; pwr_enable_backup_domain_write_protect(); } -uint32_t backup_read(enum BackupRegister reg) { - uint32_t value = (uint32_t)RTC_BKP_DR((int)reg) - | ((uint32_t)RTC_BKP_DR((int)reg+1) << 16); - return value; +uint16_t backup_read(enum BackupRegister reg) { + return RTC_BKP_DR((int)reg); } diff --git a/src/stm32f103/backup.h b/src/stm32f103/backup.h index 739dced..5e265a9 100644 --- a/src/stm32f103/backup.h +++ b/src/stm32f103/backup.h @@ -32,7 +32,7 @@ enum BackupRegister { BKP10, }; -extern void backup_write(enum BackupRegister reg, uint32_t value); -extern uint32_t backup_read(enum BackupRegister reg); +extern void backup_write(enum BackupRegister reg, uint16_t value); +extern uint16_t backup_read(enum BackupRegister reg); #endif diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index ba924b5..557f630 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -60,7 +60,7 @@ _Static_assert((FLASH_BASE + FLASH_SIZE_OVERRIDE >= APP_BASE_ADDRESS), #endif #ifndef CMD_BOOT -#define CMD_BOOT 0x544F4F42UL +#define CMD_BOOT 0x4F42UL #endif void target_clock_setup(void) { @@ -166,7 +166,7 @@ const usbd_driver* target_usb_init(void) { bool target_get_force_bootloader(void) { bool force = false; /* Check the RTC backup register */ - uint32_t cmd = backup_read(REG_BOOT); + uint16_t cmd = backup_read(REG_BOOT); if (cmd == CMD_BOOT) { force = true; } From bc98fc40c662a5d2a33a7449c292dee21019d918 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Fri, 10 Sep 2021 23:24:26 +0300 Subject: [PATCH 51/66] Add DFU_WILL_DETACH option for automatic detach/reset and enable it by default Enabling DFU_WILL_DETACH will set bitManifestationTolerant = 0 and bitWillDetach = 1, disabling it will set the opposite. If DFU_WILL_DETACH is set, the target will automatically detach and reset following a successful reprogramming, and if it's not set, the target will enter the dfuIDLE state. Both of these behaviors are described in the DFU 1.1 spec. DFU_WILL_DETACH is enabled by default for WebUSB compatibility, as some platforms will apparently not instruct the target to reset after flashing. --- src/dfu.c | 15 +++++++++++++-- src/dfu.h | 5 +++++ src/usb_conf.c | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index eb843db..aba22a5 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -24,9 +24,9 @@ #include #include -#include "dfu.h" #include "config.h" #include "usb_conf.h" +#include "dfu.h" #include "dfu_defs.h" #include "target.h" #include "dapboot.h" @@ -40,7 +40,8 @@ const struct usb_dfu_descriptor dfu_function = { .bDescriptorType = DFU_FUNCTIONAL, .bmAttributes = ((DFU_DOWNLOAD_AVAILABLE ? USB_DFU_CAN_DOWNLOAD : 0) | (DFU_UPLOAD_AVAILABLE ? USB_DFU_CAN_UPLOAD : 0) | - USB_DFU_MANIFEST_TOLERANT), + (DFU_WILL_DETACH ? 0 : USB_DFU_MANIFEST_TOLERANT) | + (DFU_WILL_DETACH ? USB_DFU_WILL_DETACH : 0)), .wDetachTimeout = 255, .wTransferSize = TARGET_DFU_WTRANSFERSIZE, .bcdDFUVersion = 0x0110, @@ -87,12 +88,14 @@ static inline void dfu_set_status(enum dfu_status status) { current_dfu_status = status; } +#if !DFU_WILL_DETACH static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; dfu_set_state(STATE_DFU_MANIFEST_SYNC); } +#endif static void dfu_on_detach_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; @@ -140,6 +143,7 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data } } +#if !DFU_WILL_DETACH static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -148,6 +152,7 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data dfu_manifest_request_callback(); } } +#endif static enum usbd_request_return_codes dfu_control_class_request(usbd_device *usbd_dev, @@ -179,6 +184,7 @@ dfu_control_class_request(usbd_device *usbd_dev, *complete = &dfu_on_download_request; break; } +#if !DFU_WILL_DETACH case STATE_DFU_MANIFEST_SYNC: { if (validate_application()) { dfu_set_state(STATE_DFU_IDLE); @@ -188,6 +194,7 @@ dfu_control_class_request(usbd_device *usbd_dev, } break; } +#endif #endif default: { break; @@ -227,7 +234,11 @@ dfu_control_class_request(usbd_device *usbd_dev, memcpy(dfu_download_buffer, *buf, dfu_download_size); dfu_set_state(STATE_DFU_DNLOAD_SYNC); } else { +#if DFU_WILL_DETACH + *complete = &dfu_on_detach_complete; +#else *complete = &dfu_on_download_complete; +#endif } break; } diff --git a/src/dfu.h b/src/dfu.h index 30e9dbe..a8a5e1b 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -22,6 +22,11 @@ #include #include +// For WebUSB compatibility +#ifndef DFU_WILL_DETACH +#define DFU_WILL_DETACH 1 +#endif + extern const struct usb_dfu_descriptor dfu_function; typedef void (*GenericCallback)(void); diff --git a/src/usb_conf.c b/src/usb_conf.c index dc88e6a..591f17e 100644 --- a/src/usb_conf.c +++ b/src/usb_conf.c @@ -22,11 +22,11 @@ #include #include "target.h" -#include "dfu.h" #include "webusb.h" #include "config.h" #include "usb_conf.h" +#include "dfu.h" static const struct usb_device_descriptor dev = { .bLength = USB_DT_DEVICE_SIZE, From 4af4c1a97aede1bacc14a15319e6d256ef4294de Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Fri, 10 Sep 2021 23:50:50 +0300 Subject: [PATCH 52/66] Update config.h for BTTSKRMINIE3V2{,_USBMOD} to accommodate changes from other PRs --- src/stm32f103/skrminie3v2/config.h | 7 +++++-- src/stm32f103/skrminie3v2_usbmod/config.h | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/stm32f103/skrminie3v2/config.h b/src/stm32f103/skrminie3v2/config.h index 39fba7d..2c3796c 100644 --- a/src/stm32f103/skrminie3v2/config.h +++ b/src/stm32f103/skrminie3v2/config.h @@ -32,6 +32,9 @@ #ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 #endif +#ifndef DFU_WILL_DETACH +#define DFU_WILL_DETACH 0 +#endif /* There is a PCB-mounted status LED, but it's unreadable when the board is installed, * and additionally it's wired to SWDIO so toggling it manually is a bad idea */ @@ -89,8 +92,8 @@ #define CMD_BOOT 1 #endif -#ifndef USB_ALT -#define USB_ALT 2 +#ifndef USB_DFU_ALTN +#define USB_DFU_ALTN 2 #endif #endif diff --git a/src/stm32f103/skrminie3v2_usbmod/config.h b/src/stm32f103/skrminie3v2_usbmod/config.h index 089a6f3..a2cf86a 100644 --- a/src/stm32f103/skrminie3v2_usbmod/config.h +++ b/src/stm32f103/skrminie3v2_usbmod/config.h @@ -41,6 +41,9 @@ #ifndef DFU_DOWNLOAD_AVAILABLE #define DFU_DOWNLOAD_AVAILABLE 1 #endif +#ifndef DFU_WILL_DETACH +#define DFU_WILL_DETACH 0 +#endif /* There is a PCB-mounted status LED, but it's unreadable when the board is installed, and additionally it's wired to * SWDIO so toggling it manually is a bad idea. To restore reliable SWD communication, desolder either the status LED @@ -96,8 +99,8 @@ #define CMD_BOOT 1 #endif -#ifndef USB_ALT -#define USB_ALT 2 +#ifndef USB_DFU_ALTN +#define USB_DFU_ALTN 2 #endif #endif From da0ec9df402aa41fcd6f11b6b658a5f61cbe8e16 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Fri, 10 Sep 2021 18:19:59 -0700 Subject: [PATCH 53/66] Adjust DFU_WILL_DETACH behavior Always enter dfuMANIFEST-SYNC, but subsequently reset to manifest if the firmware is valid. Call the target manifest callback if provided, but reset in case the callback is absent or fails to reset the device. --- src/dapboot.c | 2 +- src/dfu.c | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/dapboot.c b/src/dapboot.c index 3c415fd..4c851cf 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -75,7 +75,7 @@ int main(void) { } usbd_device* usbd_dev = usb_setup(); - dfu_setup(usbd_dev, NULL, NULL, NULL); + dfu_setup(usbd_dev, target_manifest_app, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); diff --git a/src/dfu.c b/src/dfu.c index aba22a5..67fe518 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -88,14 +88,12 @@ static inline void dfu_set_status(enum dfu_status status) { current_dfu_status = status; } -#if !DFU_WILL_DETACH static void dfu_on_download_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; - + dfu_set_state(STATE_DFU_MANIFEST_SYNC); } -#endif static void dfu_on_detach_complete(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; @@ -143,7 +141,7 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data } } -#if !DFU_WILL_DETACH +#if DFU_WILL_DETACH static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -151,6 +149,10 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data if (dfu_manifest_request_callback) { dfu_manifest_request_callback(); } + + /* Reset and maybe launch the application if the manifest callback + didn't already do it */ + scb_reset_system(); } #endif @@ -184,17 +186,21 @@ dfu_control_class_request(usbd_device *usbd_dev, *complete = &dfu_on_download_request; break; } -#if !DFU_WILL_DETACH case STATE_DFU_MANIFEST_SYNC: { if (validate_application()) { - dfu_set_state(STATE_DFU_IDLE); +#if DFU_WILL_DETACH + /* Manifest by resetting after responding */ + dfu_set_state(STATE_DFU_MANIFEST); *complete = &dfu_on_manifest_request; +#else + /* Return to the idle state and await commands */ + dfu_set_state(STATE_DFU_IDLE); +#endif } else { dfu_set_status(DFU_STATUS_ERR_FIRMWARE); } break; } -#endif #endif default: { break; @@ -234,11 +240,7 @@ dfu_control_class_request(usbd_device *usbd_dev, memcpy(dfu_download_buffer, *buf, dfu_download_size); dfu_set_state(STATE_DFU_DNLOAD_SYNC); } else { -#if DFU_WILL_DETACH - *complete = &dfu_on_detach_complete; -#else *complete = &dfu_on_download_complete; -#endif } break; } From 9bf8c4d8f56a3bf572a9db998a807e34216195f6 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Fri, 10 Sep 2021 18:22:24 -0700 Subject: [PATCH 54/66] Detect USB requests and reset or enter error state Per the DFU spec, a USB reset should cause the device to either reenumerate as the application or enter an error state if invalid. Register a USB reset callback to detect and handle USB resets, taking care not to trigger the first time the bootloader is enumerated. Run the manifest callback if present when manifesting via USB reset. --- src/dfu.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/dfu.c b/src/dfu.c index 67fe518..e376afc 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -331,6 +331,10 @@ dfu_control_class_request(usbd_device *usbd_dev, return status; } +/* Track dfu enumeration status to distinguish between the first USB + reset after bootup versus a subsequent USB reset and re-enumeration */ +static bool dfu_enumerated = false; + static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { (void)wValue; @@ -339,6 +343,35 @@ static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, dfu_control_class_request); + + dfu_enumerated = true; +} + +static void dfu_on_usb_reset(void) { + /* Ignore the first USB reset after boot, before anyone has had + a chance to do anything to the device; otherwise we'd never + enter DFU mode when the application is valid */ + if (!dfu_enumerated) { + return; + } + + /* On subsequent USB reset requests, either manifest by resetting + or enter the error state if firmware is invalid, per the spec */ + if (validate_application()) { + /* Manifest by resetting after responding */ + dfu_set_state(STATE_DFU_MANIFEST); + + if (dfu_manifest_request_callback) { + dfu_manifest_request_callback(); + } + + /* Reset and launch the application if the manifest callback + didn't already do it */ + scb_reset_system(); + } else { + /* Enter the error state and await further commands */ + dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + } } void dfu_setup(usbd_device* usbd_dev, @@ -349,6 +382,7 @@ void dfu_setup(usbd_device* usbd_dev, dfu_state_change_callback = on_state_change; dfu_status_change_callback = on_status_change; + usbd_register_reset_callback(usbd_dev, dfu_on_usb_reset); usbd_register_set_config_callback(usbd_dev, dfu_set_config); current_dfu_state = STATE_DFU_IDLE; current_dfu_status = DFU_STATUS_OK; From 191a0925a9971389d7aaae236d6a55890d50ed3b Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Fri, 10 Sep 2021 18:52:56 -0700 Subject: [PATCH 55/66] Refine manifest request event criteria If the firmware has been written to at least once, treat a detach/USB reset as an implicit manifestation request and run the manifest request callback. Otherwise treat it as a detach request and simply reset. --- src/dfu.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index e376afc..e75480e 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -51,6 +51,8 @@ static enum dfu_state current_dfu_state; static enum dfu_status current_dfu_status; static size_t current_dfu_offset; +static bool dfu_modified_firmware = false; + static uint8_t dfu_download_buffer[USB_CONTROL_BUF_SIZE]; static size_t dfu_download_size; @@ -131,6 +133,9 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data bool ok = target_flash_program_array(dest, data, dfu_download_size/2); target_flash_lock(); + /* Record that we touched the application firmware */ + dfu_modified_firmware = true; + if (ok) { current_dfu_offset += dfu_download_size; /* We could go back to STATE_DFU_DNLOAD_SYNC, but then @@ -141,7 +146,6 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data } } -#if DFU_WILL_DETACH static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data* req) { (void)usbd_dev; (void)req; @@ -154,7 +158,6 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data didn't already do it */ scb_reset_system(); } -#endif static enum usbd_request_return_codes dfu_control_class_request(usbd_device *usbd_dev, @@ -314,7 +317,11 @@ dfu_control_class_request(usbd_device *usbd_dev, #endif case DFU_DETACH: { - *complete = &dfu_on_detach_complete; + if (dfu_modified_firmware) { + *complete = &dfu_on_manifest_request; + } else { + *complete = &dfu_on_detach_complete; + } status = USBD_REQ_HANDLED; break; } @@ -355,14 +362,16 @@ static void dfu_on_usb_reset(void) { return; } - /* On subsequent USB reset requests, either manifest by resetting + /* On subsequent USB reset requests, either reset/manifest or enter the error state if firmware is invalid, per the spec */ if (validate_application()) { - /* Manifest by resetting after responding */ - dfu_set_state(STATE_DFU_MANIFEST); + if (dfu_modified_firmware) { + /* Manifest by resetting after responding */ + dfu_set_state(STATE_DFU_MANIFEST); - if (dfu_manifest_request_callback) { - dfu_manifest_request_callback(); + if (dfu_manifest_request_callback) { + dfu_manifest_request_callback(); + } } /* Reset and launch the application if the manifest callback From 178d90974c9b09687f6115fe767c494d8ff3b8e1 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Sat, 11 Sep 2021 21:24:28 +0300 Subject: [PATCH 56/66] Implement proper manifestation according to the spec The initial behavior of entering dfuMANIFEST-SYNC was incorrect, sorry about that. I've now thoroughly read the DFU spec, which actually states that dfuMANIFEST-SYNC shall be entered *twice* after a download operation if bitMainfestationTolerant = 1 (i.e. DFU_WILL_DETACH == 0), once after the download completes, which begins manifestation by transitioning to dfuMANIFEST, and again after manifestation has completed successfully, and only after that should the transition to dfuIDLE be made. This means that there should be *two* DFU_GETSTATUS calls from the host, and indeed dfu-util does exactly this (verified using Wireshark). Manifesting on USB reset or on detach is not according to spec. It should only happen right after a download operation has finished. For this I've overhauled the dfu_on_manifest_request() function to work as it should with both DFU_WILL_DETACH == 1 and DFU_WILL_DETACH == 0. Validation should actually be part of manifestation, and I've now added it as the manifestation callback (since there is no other manifestation work to be done). The scb_reset_system() call in the USB reset callback is what you need to perform detach-by-USB-reset, but to avoid having random reset calls in multiple places it makes much more sense to call the actual dfu_on_detach_complete() callback which will then perform the reset. The USB reset callback should also not validate, e.g. a blank flash with just the bootloader should not trigger a firmware error status, so I've removed that logic. --- src/dapboot.c | 7 ++--- src/dfu.c | 80 ++++++++++++++++++++++----------------------------- src/dfu.h | 4 +-- 3 files changed, 38 insertions(+), 53 deletions(-) diff --git a/src/dapboot.c b/src/dapboot.c index 4c851cf..551bc71 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -32,10 +32,7 @@ static inline void __set_MSP(uint32_t topOfMainStack) { } bool validate_application(void) { - if (((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000) { - return true; - } - return false; + return ((uint32_t)(APP_INITIAL_STACK) & 0x2FFE0000) == 0x20000000; } static void jump_to_application(void) __attribute__ ((noreturn)); @@ -75,7 +72,7 @@ int main(void) { } usbd_device* usbd_dev = usb_setup(); - dfu_setup(usbd_dev, target_manifest_app, NULL, NULL); + dfu_setup(usbd_dev, validate_application, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); diff --git a/src/dfu.c b/src/dfu.c index e75480e..8923130 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -51,13 +51,13 @@ static enum dfu_state current_dfu_state; static enum dfu_status current_dfu_status; static size_t current_dfu_offset; -static bool dfu_modified_firmware = false; +static bool manifestation_complete = false; static uint8_t dfu_download_buffer[USB_CONTROL_BUF_SIZE]; static size_t dfu_download_size; /* User callbacks */ -static GenericCallback dfu_manifest_request_callback = NULL; +static ManifestationCallback dfu_manifest_request_callback = NULL; static StateChangeCallback dfu_state_change_callback = NULL; static StatusChangeCallback dfu_status_change_callback = NULL; @@ -133,9 +133,6 @@ static void dfu_on_download_request(usbd_device* usbd_dev, struct usb_setup_data bool ok = target_flash_program_array(dest, data, dfu_download_size/2); target_flash_lock(); - /* Record that we touched the application firmware */ - dfu_modified_firmware = true; - if (ok) { current_dfu_offset += dfu_download_size; /* We could go back to STATE_DFU_DNLOAD_SYNC, but then @@ -151,12 +148,22 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data (void)req; if (dfu_manifest_request_callback) { - dfu_manifest_request_callback(); + /* The manifestation callback returns a boolean indicating if it succeeded */ + if (dfu_manifest_request_callback()) { + manifestation_complete = true; + dfu_set_state(STATE_DFU_MANIFEST_SYNC); + } else { + dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + return; /* Avoid resetting on error */ + } } - /* Reset and maybe launch the application if the manifest callback - didn't already do it */ - scb_reset_system(); +#if DFU_WILL_DETACH + /* DFU_WILL_DETACH being enabled equates to transitioning to the dfuMANIFEST-WAIT-RESET state, + * which combined with bitWillDetach being set with DFU_WILL_DETACH means that the device should + * generate a detach-attach sequence and enter the application, i.e. reset itself, here */ + dfu_on_detach_complete(NULL, NULL); +#endif } static enum usbd_request_return_codes @@ -190,17 +197,18 @@ dfu_control_class_request(usbd_device *usbd_dev, break; } case STATE_DFU_MANIFEST_SYNC: { - if (validate_application()) { -#if DFU_WILL_DETACH - /* Manifest by resetting after responding */ - dfu_set_state(STATE_DFU_MANIFEST); - *complete = &dfu_on_manifest_request; -#else - /* Return to the idle state and await commands */ + /* According to the DFU spec the dfuMANIFEST-SYNC state is entered twice, + * once after the download completes, and again after manifestation if + * the device is manifestation tolerant (DFU_WILL_DETACH == 0) */ + if (manifestation_complete) { + /* Only enter idle state after manifestation has completed successfully */ + manifestation_complete = false; dfu_set_state(STATE_DFU_IDLE); -#endif } else { - dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + /* Perform manifestation after download as described in the + * spec regardless of if DFU_WILL_DETACH is enabled or not */ + dfu_set_state(STATE_DFU_MANIFEST); + *complete = &dfu_on_manifest_request; } break; } @@ -317,11 +325,7 @@ dfu_control_class_request(usbd_device *usbd_dev, #endif case DFU_DETACH: { - if (dfu_modified_firmware) { - *complete = &dfu_on_manifest_request; - } else { - *complete = &dfu_on_detach_complete; - } + *complete = &dfu_on_detach_complete; status = USBD_REQ_HANDLED; break; } @@ -355,36 +359,20 @@ static void dfu_set_config(usbd_device* usbd_dev, uint16_t wValue) { } static void dfu_on_usb_reset(void) { - /* Ignore the first USB reset after boot, before anyone has had - a chance to do anything to the device; otherwise we'd never - enter DFU mode when the application is valid */ + /* Ignore all USB resets until the DFU control callback has been registered, since + * reset callback will fire once as the USB connection is established. Without this + * the target enters a reset loop when trying to enter the bootloader. */ if (!dfu_enumerated) { return; } - /* On subsequent USB reset requests, either reset/manifest - or enter the error state if firmware is invalid, per the spec */ - if (validate_application()) { - if (dfu_modified_firmware) { - /* Manifest by resetting after responding */ - dfu_set_state(STATE_DFU_MANIFEST); - - if (dfu_manifest_request_callback) { - dfu_manifest_request_callback(); - } - } - - /* Reset and launch the application if the manifest callback - didn't already do it */ - scb_reset_system(); - } else { - /* Enter the error state and await further commands */ - dfu_set_status(DFU_STATUS_ERR_FIRMWARE); - } + /* Perform a DFU detach (which resets the target), this enables issuing a USB bus + * reset as an alternative means to submitting a DFU_DETACH command post-download. */ + dfu_on_detach_complete(NULL, NULL); } void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_manifest_request, + ManifestationCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change) { dfu_manifest_request_callback = on_manifest_request; diff --git a/src/dfu.h b/src/dfu.h index a8a5e1b..d447b22 100644 --- a/src/dfu.h +++ b/src/dfu.h @@ -29,12 +29,12 @@ extern const struct usb_dfu_descriptor dfu_function; -typedef void (*GenericCallback)(void); +typedef bool (*ManifestationCallback)(void); typedef void (*StateChangeCallback)(enum dfu_state); typedef void (*StatusChangeCallback)(enum dfu_status); extern void dfu_setup(usbd_device* usbd_dev, - GenericCallback on_manifest_request, + ManifestationCallback on_manifest_request, StateChangeCallback on_state_change, StatusChangeCallback on_status_change); From 7e25c1e98ee6942e60a3584616b3801d0898b1fb Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sat, 11 Sep 2021 13:12:20 -0700 Subject: [PATCH 57/66] Replace target_manifest_app with target_pre_detach Provide a target-specific hook to run just before detaching/rebooting. The manifested parameter can be used to distinguish between resetting to run existing vs new firmware. --- src/dfu.c | 24 ++++++++++++++++-------- src/dummy.c | 15 ++++++++++----- src/target.h | 2 +- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/dfu.c b/src/dfu.c index 8923130..b8fa42f 100644 --- a/src/dfu.c +++ b/src/dfu.c @@ -101,6 +101,9 @@ static void dfu_on_detach_complete(usbd_device* usbd_dev, struct usb_setup_data* (void)usbd_dev; (void)req; + /* Run the target-specific pre-detach hook before resetting */ + target_pre_detach(manifestation_complete); + /* Reset and maybe launch the application */ scb_reset_system(); } @@ -149,13 +152,17 @@ static void dfu_on_manifest_request(usbd_device* usbd_dev, struct usb_setup_data if (dfu_manifest_request_callback) { /* The manifestation callback returns a boolean indicating if it succeeded */ - if (dfu_manifest_request_callback()) { - manifestation_complete = true; - dfu_set_state(STATE_DFU_MANIFEST_SYNC); - } else { - dfu_set_status(DFU_STATUS_ERR_FIRMWARE); - return; /* Avoid resetting on error */ - } + manifestation_complete = dfu_manifest_request_callback(); + } else { + /* Assume manifestation success */ + manifestation_complete = true; + } + + if (manifestation_complete) { + dfu_set_state(STATE_DFU_MANIFEST_SYNC); + } else { + dfu_set_status(DFU_STATUS_ERR_FIRMWARE); + return; /* Avoid resetting on error */ } #if DFU_WILL_DETACH @@ -202,7 +209,6 @@ dfu_control_class_request(usbd_device *usbd_dev, * the device is manifestation tolerant (DFU_WILL_DETACH == 0) */ if (manifestation_complete) { /* Only enter idle state after manifestation has completed successfully */ - manifestation_complete = false; dfu_set_state(STATE_DFU_IDLE); } else { /* Perform manifestation after download as described in the @@ -239,6 +245,8 @@ dfu_control_class_request(usbd_device *usbd_dev, dfu_download_size = req->wLength; memcpy(dfu_download_buffer, *buf, dfu_download_size); dfu_set_state(STATE_DFU_DNLOAD_SYNC); + /* Reset manifestation progress on new download */ + manifestation_complete = false; } else { dfu_set_status(DFU_STATUS_ERR_STALLEDPKT); usbd_ep_stall_set(usbd_dev, 0x00, 1); diff --git a/src/dummy.c b/src/dummy.c index 6589e57..b10df11 100644 --- a/src/dummy.c +++ b/src/dummy.c @@ -24,8 +24,8 @@ void target_get_serial_number(char* dest, size_t max_chars) __attribute__((weak)); void target_log(const char* str) __attribute__((weak)); -void target_manifest_app(void) __attribute__((weak)); void target_pre_main(void) __attribute__((weak)); +void target_pre_detach(bool manifested) __attribute__((weak)); size_t target_get_timeout(void) __attribute__((weak)); void target_get_serial_number(char* dest, size_t max_chars) { @@ -39,15 +39,20 @@ void target_log(const char* str) { (void)str; } -void target_manifest_app(void) { - scb_reset_system(); -} - void target_pre_main(void) { } +void target_pre_detach(bool manifested) { + /* This runs just before executing a reboot in response to a USB bus reset + or a detach request. + If new firmware was successfully downloaded, manifested is set to true. + This can be used to set flags or blink LEDs before rebooting. + */ + (void)manifested; +} + size_t target_get_timeout(void) { return 100; diff --git a/src/target.h b/src/target.h index 7e671cf..2f748d0 100644 --- a/src/target.h +++ b/src/target.h @@ -31,12 +31,12 @@ extern void target_get_serial_number(char* dest, size_t max_chars); extern size_t target_get_max_firmware_size(void); extern void target_log(const char* str); extern void target_relocate_vector_table(void); -extern void target_manifest_app(void); extern void target_flash_unlock(void); extern void target_flash_lock(void); extern bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count); extern void target_pre_main(void); +extern void target_pre_detach(bool manifested); extern size_t target_get_timeout(void); #endif From 1013c98e013e8004158c2837776fc92f7a4a0041 Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sat, 11 Sep 2021 13:42:49 -0700 Subject: [PATCH 58/66] Update README with bootloader changes Document new STM32F103 bootloader backup register changes and version changeover Document DFU_WILL_DETACH behavioral differences --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5cecc3e..3bbec70 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,11 @@ The high memory bootloaders do not use the lower part of the flash, so you only ### Switching to the bootloader -The bootloader can be built to look for arbitrary patterns, but the default for the STM32F103 target looks for a magic value stored in the RTC backup registers. Writing the magic value and then resetting will run the bootloader instead of the main application. +The bootloader can be built to look for arbitrary patterns, but the default looks for a magic value stored in the RTC backup registers. Writing the magic value and then resetting will run the bootloader instead of the main application. -The bootloader currently looks for `0x544F` in RTC backup register 1 and `0x4F42` in RTC backup register 0 (together they spell "BOOT" in ASCII). +In version v1.11 and earlier, the bootloader for STM32F103 targets looks for `0x544F` in RTC backup register 1 and `0x4F42` in RTC backup register 0 (together they spell "BOOT" in ASCII). In the current master branch and any subsequent releases, the bootloader will only use RTC backup register 0 and check for `0x4F42` on targets with 16-bit backup registers and `0x544F4F42` on targets with 32-bit backup registers. + +The backup register and bootloader command word can be customized with the `REG_BOOT` and `CMD_BOOT` defines respectively. You can also use a button to stay in bootloader while booting. It's configured using `HAVE_BUTTON` define. If your button has a debounce capacitor, you can use `BUTTON_SAMPLE_DELAY_CYCLES` define to specify how many cycles to wait before sampling the I/O pin, by default it is approximately 20ms in a 72Mhz MCU. @@ -87,6 +89,13 @@ To customize the WebUSB landing page, you can use the `LANDING_PAGE_URL` define. Note that the URL scheme shoul not be part of the `LANDING_PAGE_URL` string. As of this writing, it is hardcoded to HTTPS. +### Manifestation behavior +There are two different manifestation behaviors that can be selected using the `DFU_WILL_DETACH` define. By default, `DFU_WILL_DETACH` is set to 1 for backwards compatibility with existing behavior. + +When `DFU_WILL_DETACH` is enabled, the bootloader autonomously reboot into the new firmware after it has been successfully downloaded and passes the basic validation check. This can be helpful on platforms such as Windows where it is not possible to generate a USB bus reset to signal that the bootloader should switch to the application. + +When `DFU_WILL_DETACH` is disabled, the bootloader will return to the idle state after validating the firmware. The DFU host application can then perform other operations or reboot the target into the new firmware by issuing a DFU detach request or generating a USB bus reset. (This can be done with `dfu-util` by passing the `-R` flag). + ## USB VID/PID The default USB VID/PID pair ([1209/DB42](http://pid.codes/1209/DB42/)) is allocated through the [pid.codes](http://pid.codes/) open-source USB PID program. From 3f5f3876d67f48533982ae5f89daf254ae1b375e Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sat, 11 Sep 2021 15:57:09 -0700 Subject: [PATCH 59/66] Update RTC backup register notes Finalize version changeover at v1.20 Use RTC backup register numbers consistent with RM0008 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bbec70..e633da2 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ The high memory bootloaders do not use the lower part of the flash, so you only ### Switching to the bootloader The bootloader can be built to look for arbitrary patterns, but the default looks for a magic value stored in the RTC backup registers. Writing the magic value and then resetting will run the bootloader instead of the main application. -In version v1.11 and earlier, the bootloader for STM32F103 targets looks for `0x544F` in RTC backup register 1 and `0x4F42` in RTC backup register 0 (together they spell "BOOT" in ASCII). In the current master branch and any subsequent releases, the bootloader will only use RTC backup register 0 and check for `0x4F42` on targets with 16-bit backup registers and `0x544F4F42` on targets with 32-bit backup registers. +In version v1.11 and earlier, the bootloader for STM32F103 targets looks for `0x544F` in RTC backup register 2 and `0x4F42` in RTC backup register 1 (together they spell "BOOT" in ASCII). From v1.20 on, the bootloader will only use RTC backup register 1 and check for `0x4F42` on targets with 16-bit backup registers and `0x544F4F42` on targets with 32-bit backup registers. The backup register and bootloader command word can be customized with the `REG_BOOT` and `CMD_BOOT` defines respectively. From 0ca77075909714d668a3c7138562b7926835541f Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Mon, 7 Mar 2022 15:09:47 -0800 Subject: [PATCH 60/66] Add a new 'target_post_setup' hook that targets can use for their own custom initialization --- src/dapboot.c | 3 ++- src/dummy.c | 7 +++++++ src/target.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/dapboot.c b/src/dapboot.c index 551bc71..2b66151 100644 --- a/src/dapboot.c +++ b/src/dapboot.c @@ -75,7 +75,8 @@ int main(void) { dfu_setup(usbd_dev, validate_application, NULL, NULL); webusb_setup(usbd_dev); winusb_setup(usbd_dev); - + target_post_setup(); + while (1) { usbd_poll(usbd_dev); } diff --git a/src/dummy.c b/src/dummy.c index b10df11..25e0f2b 100644 --- a/src/dummy.c +++ b/src/dummy.c @@ -26,6 +26,7 @@ void target_get_serial_number(char* dest, size_t max_chars) __attribute__((weak) void target_log(const char* str) __attribute__((weak)); void target_pre_main(void) __attribute__((weak)); void target_pre_detach(bool manifested) __attribute__((weak)); +void target_post_setup(void) __attribute__((weak)); size_t target_get_timeout(void) __attribute__((weak)); void target_get_serial_number(char* dest, size_t max_chars) { @@ -44,6 +45,12 @@ void target_pre_main(void) } +void target_post_setup(void) +{ + /* This runs just before starting to listen to USB */ +} + + void target_pre_detach(bool manifested) { /* This runs just before executing a reboot in response to a USB bus reset or a detach request. diff --git a/src/target.h b/src/target.h index 2f748d0..429177e 100644 --- a/src/target.h +++ b/src/target.h @@ -36,6 +36,7 @@ extern void target_flash_lock(void); extern bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count); extern void target_pre_main(void); +extern void target_post_setup(void); extern void target_pre_detach(bool manifested); extern size_t target_get_timeout(void); From fd1acb574e78f2ac367442fdd902a0402053443a Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Sat, 8 Jul 2023 16:20:13 +0300 Subject: [PATCH 61/66] Remove BTTSKRMINIE3V2_USBMOD, use BTTSKRMINIE3V2 instead For the past two years I've had only occasional issues with a BTTSKRMINIE3V2_USBMOD build my the SKR MINI, but after upgrading to a faster Pi (64-bit OS) for Klipper, some major problems with this mod became evident. Hard-wiring the USB D+ pullup to 3V3 causes the host to enumerate it as soon as a soft reset is issued, whereby the firmware (and this bootloader) have not had time to properly initialize and get USB ready. This stalls USB hubs, times out the Ethernet controller on Raspberry Pis, and causes all kinds of other issues, thus, I'm discouraging its use and removing the target. Regarding the debugging difficulties motivating BTTSKRMINIE3V2_USBMOD, they can be solved with a proper debugging setup. I'm able to successfully debug the MCU even with the USB pullup MOSFET and the STATUS LED attached to SWDCLK and SWDIO respectively using a FT232H. As such, the BTTSKRMINIE3V2 target should be used instead, and I've also re-added control of the STATUS LED to indicate bootloader presence. --- README.md | 1 - release.Makefile | 16 +--- src/stm32f103/skrminie3v2/config.h | 18 +++- src/stm32f103/skrminie3v2_usbmod/config.h | 106 ---------------------- src/targets.mk | 13 --- 5 files changed, 14 insertions(+), 140 deletions(-) delete mode 100644 src/stm32f103/skrminie3v2_usbmod/config.h diff --git a/README.md b/README.md index e633da2..df3c852 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ To build other targets, you can override the |`OLIMEXSTM32H103` | Olimex STM32-H103 | https://www.olimex.com/Products/ARM/ST/STM32-H103/ | |`BLUEPILLPLUSSTM32` | Bluepill with USB C | https://github.com/WeActTC/BluePill-Plus/ | |`BTTSKRMINIE3V2` | BigTreeTech SKR MINI E3 V2.0 (3D printer motherboard) | https://github.com/bigtreetech/BIGTREETECH-SKR-mini-E3 | -|`BTTSKRMINIE3V2_USBMOD` | BTT SKR MINI E3 V2.0 with USB pullup removed | https://github.com/bigtreetech/BIGTREETECH-SKR-mini-E3 | For the above targets there are some potential variants that can be added to the target name based on what the target supports: diff --git a/release.Makefile b/release.Makefile index 34a29c1..dbc6779 100644 --- a/release.Makefile +++ b/release.Makefile @@ -35,7 +35,6 @@ all: dapboot-bluepill.bin \ dapboot-olimexstm32h103.bin \ dapboot-bluepillplusstm32.bin \ dapboot-bttskrminie3v2.bin \ - dapboot-bttskrminie3v2-usbmod.bin \ dapboot-bluepill-high.bin \ dapboot-maplemini-high.bin \ dapboot-stlink-high.bin \ @@ -46,8 +45,7 @@ all: dapboot-bluepill.bin \ dapboot-stlink-high-128.bin \ dapboot-olimexstm32h103-high-128.bin \ dapboot-bluepillplusstm32-high-128.bin \ - dapboot-bttskrminie3v2-high-256.bin \ - dapboot-bttskrminie3v2-usbmod-high-256.bin + dapboot-bttskrminie3v2-high-256.bin clean: $(Q)$(RM) $(BUILD_DIR)/*.bin @@ -94,12 +92,6 @@ dapboot-bttskrminie3v2.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2 -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) -dapboot-bttskrminie3v2-usbmod.bin: | $(BUILD_DIR) - @printf " BUILD $(@)\n" - $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD -C src/ clean - $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD -C src/ - $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) - dapboot-bluepill-high.bin: | $(BUILD_DIR) @printf " BUILD $(@)\n" $(Q)$(MAKE) TARGET=BLUEPILL_HIGH -C src/ clean @@ -165,9 +157,3 @@ dapboot-bttskrminie3v2-high-256.bin: | $(BUILD_DIR) $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_HIGH_256 -C src/ clean $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_HIGH_256 -C src/ $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) - -dapboot-bttskrminie3v2-usbmod-high-256.bin: | $(BUILD_DIR) - @printf " BUILD $(@)\n" - $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD_HIGH_256 -C src/ clean - $(Q)$(MAKE) TARGET=BTTSKRMINIE3V2_USBMOD_HIGH_256 -C src/ - $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) diff --git a/src/stm32f103/skrminie3v2/config.h b/src/stm32f103/skrminie3v2/config.h index 2c3796c..8ebc381 100644 --- a/src/stm32f103/skrminie3v2/config.h +++ b/src/stm32f103/skrminie3v2/config.h @@ -36,10 +36,18 @@ #define DFU_WILL_DETACH 0 #endif -/* There is a PCB-mounted status LED, but it's unreadable when the board is installed, - * and additionally it's wired to SWDIO so toggling it manually is a bad idea */ +/* STATUS LED */ #ifndef HAVE_LED -#define HAVE_LED 0 +#define HAVE_LED 1 +#endif +#ifndef LED_OPEN_DRAIN +#define LED_OPEN_DRAIN 0 +#endif +#ifndef LED_GPIO_PORT +#define LED_GPIO_PORT GPIOA +#endif +#ifndef LED_GPIO_PIN +#define LED_GPIO_PIN GPIO13 #endif /* Display encoder button (BTN-ENC) on PA15, no external pullup */ @@ -82,8 +90,8 @@ #define USES_GPIOA 1 #endif -/* For stm32duino bootloader compatibility, the following options enable - * bootloader flashing using KIAUH: https://github.com/th33xitus/kiauh */ +/* For stm32duino bootloader compatibility, the following options enable bootloader flashing using Klipper: + * https://github.com/Klipper3d/klipper/blob/6d48adf9ef5d17632acf53a7e3a07964f6cfd642/src/stm32/stm32f1.c#L238 */ #ifndef REG_BOOT #define REG_BOOT BKP10 #endif diff --git a/src/stm32f103/skrminie3v2_usbmod/config.h b/src/stm32f103/skrminie3v2_usbmod/config.h deleted file mode 100644 index a2cf86a..0000000 --- a/src/stm32f103/skrminie3v2_usbmod/config.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2021, Dennis Marttinen - * - * Permission to use, copy, modify, and/or distribute this software - * for any purpose with or without fee is hereby granted, provided - * that the above copyright notice and this permission notice - * appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * ATTENTION! Using the USBMOD target requires hardware modifications to your BTT SKR MINI E3 V2.0. These modifications - * aim to restore the broken SWD debugging support of this board by removing "unnecessary" components attached to the - * SWDIO and SWDCLK pins. Read the comments carefully, and proceed at your own risk! Flashing this firmware on an - * unmodified SKR MINI will not harm it, but USB will not work in the bootloader. Here's the schematic for the board for - * looking up the component identifiers mentioned in the comments: - * https://github.com/bigtreetech/BIGTREETECH-SKR-mini-E3/blob/master/hardware/BTT%20SKR%20MINI%20E3%20V2.0/Hardware/BTT%20SKR%20MINI%20E3%20V2.0SCHpdf.PDF - */ - -#ifndef CONFIG_H_INCLUDED -#define CONFIG_H_INCLUDED - -#ifndef APP_BASE_ADDRESS -#define APP_BASE_ADDRESS (0x08000000 + BOOTLOADER_OFFSET) -#endif -#ifndef FLASH_PAGE_SIZE -/* The BTT SKR MINI E3 V2.0 uses an STM32F103RC with 256 KiB of flash and 2 KiB pages */ -#define FLASH_PAGE_SIZE 2048 -#endif -#ifndef DFU_UPLOAD_AVAILABLE -#define DFU_UPLOAD_AVAILABLE 1 -#endif -#ifndef DFU_DOWNLOAD_AVAILABLE -#define DFU_DOWNLOAD_AVAILABLE 1 -#endif -#ifndef DFU_WILL_DETACH -#define DFU_WILL_DETACH 0 -#endif - -/* There is a PCB-mounted status LED, but it's unreadable when the board is installed, and additionally it's wired to - * SWDIO so toggling it manually is a bad idea. To restore reliable SWD communication, desolder either the status LED - * itself (D16) or the resistor for it (R90). */ -#ifndef HAVE_LED -#define HAVE_LED 0 -#endif - -/* Display encoder button (BTN-ENC) on PA15, no external pullup */ -#ifndef HAVE_BUTTON -#define HAVE_BUTTON 1 -#endif -#ifndef BUTTON_ACTIVE_HIGH -#define BUTTON_ACTIVE_HIGH 0 -#endif -#ifndef BUTTON_GPIO_PORT -#define BUTTON_GPIO_PORT GPIOA -#endif -#ifndef BUTTON_GPIO_PIN -#define BUTTON_GPIO_PIN GPIO15 -#endif -#ifndef BUTTON_USES_PULL -#define BUTTON_USES_PULL 1 -#endif -#ifndef BUTTON_SAMPLE_DELAY_CYCLES -#define BUTTON_SAMPLE_DELAY_CYCLES 1440000 -#endif - -/* The USB pullup MOSFET (U7) with its pullup resistor (R43) is wired to SWCLK on this board which makes attaching a - * debug probe nearly impossible. It is however possible to desolder these two components and bridge the source and - * drain contact pads for the MOSFET to gain debugger support. The [datasheet] for the MCU suggests that the pullup - * resistor for the USB D+ pin (R44) is enough on its own, as the MCU itself can pull the pin down to initiate a USB - * reset without an external MOSFET controlling that resistor. This does indeed work without any configuration in - * dapboot as well as both the Klipper and Marlin 3D printer firmwares. There were no problems during testing when - * performing software resets or using the reset button, the USB reset works seamlessly even when transitioning from - * dapboot to a freshly flashed firmware. - * [datasheet]: https://www.st.com/resource/en/datasheet/stm32f103rc.pdf */ -#ifndef HAVE_USB_PULLUP_CONTROL -#define HAVE_USB_PULLUP_CONTROL 0 -#endif - -#ifndef USES_GPIOA -#define USES_GPIOA 1 -#endif - -/* For stm32duino bootloader compatibility, the following options enable - * bootloader flashing using KIAUH: https://github.com/th33xitus/kiauh */ -#ifndef REG_BOOT -#define REG_BOOT BKP10 -#endif - -#ifndef CMD_BOOT -#define CMD_BOOT 1 -#endif - -#ifndef USB_DFU_ALTN -#define USB_DFU_ALTN 2 -#endif - -#endif diff --git a/src/targets.mk b/src/targets.mk index 642a8ea..70aab59 100644 --- a/src/targets.mk +++ b/src/targets.mk @@ -139,19 +139,6 @@ ifeq ($(TARGET),BTTSKRMINIE3V2_HIGH_256) ARCH = STM32F1 DEFS += -DBOOTLOADER_HIGH endif -ifeq ($(TARGET),BTTSKRMINIE3V2_USBMOD) - TARGET_COMMON_DIR := ./stm32f103 - TARGET_SPEC_DIR := ./stm32f103/skrminie3v2_usbmod - LDSCRIPT := ./stm32f103/stm32f103x8.ld - ARCH = STM32F1 -endif -ifeq ($(TARGET),BTTSKRMINIE3V2_USBMOD_HIGH_256) - TARGET_COMMON_DIR := ./stm32f103 - TARGET_SPEC_DIR := ./stm32f103/skrminie3v2_usbmod - LDSCRIPT := ./stm32f103/stm32f103xc_high.ld - ARCH = STM32F1 - DEFS += -DBOOTLOADER_HIGH -endif ifeq ($(TARGET),STM32L1_GENERIC) TARGET_COMMON_DIR := ./stm32l1 TARGET_SPEC_DIR := ./stm32l1/generic From af4d68001f5fcb4d067191614290ab8c355d28c1 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Sat, 8 Jul 2023 16:53:46 +0300 Subject: [PATCH 62/66] Fix build for high memory targets The bootloader doesn't fit into the advertised 6.5 kB anymore, resulting in linker errors for all high-memory targets: ``` /usr/lib/gcc/arm-none-eabi/8.3.1/../../../arm-none-eabi/bin/ld: dapboot.elf section `.text' will not fit in region `rom' /usr/lib/gcc/arm-none-eabi/8.3.1/../../../arm-none-eabi/bin/ld: region `rom' overflowed by 276 bytes ``` Increase the reserved high-memory area from 0x1A00 to 0x1C00 (7 KiB), with 236 bytes to spare. --- README.md | 17 +++++++++-------- src/stm32f103/stm32f103x8_high.ld | 2 +- src/stm32f103/stm32f103xb_high.ld | 2 +- src/stm32f103/stm32f103xc_high.ld | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e633da2..24a831a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ The default target is a generic STM32F103 dev board with an LED on PC13, commonl To build other targets, you can override the `TARGET` variable when invoking `make`. + cd src/ make clean make TARGET=STLINK @@ -26,14 +27,14 @@ To build other targets, you can override the For the above targets there are some potential variants that can be added to the target name based on what the target supports: -| Target Variant | Description | -| -------------- | ----------------------------------------------------- | -|` ` | Standard bootloader, using first 8kB of flash | -|`_HIGH` | High memory bootloader for 64kB chips (experimental) | -|`_HIGH_128` | High memory bootloader for 128kB chips (experimental) | -|`_HIGH_256` | High memory bootloader for 256kB chips (experimental) | +| Target Variant | Description | +| -------------- | ------------------------------------------------------- | +|` ` | Standard bootloader, using first 8 KiB of flash | +|`_HIGH` | High memory bootloader for 64 KiB chips (experimental) | +|`_HIGH_128` | High memory bootloader for 128 KiB chips (experimental) | +|`_HIGH_256` | High memory bootloader for 256 KiB chips (experimental) | -The high memory bootloader is a variation that doesn't require the application to be at an offset, the bootloader resides in the top 6.5kB of ROM and hides its reset and stack vectors inside unused entries of the application vector table. As an example, to compile for a Bluepill board with 128kB flash, use: +The high memory bootloader is a variation that doesn't require the application to be at an offset, the bootloader resides in the top 7 KiB of ROM and hides its reset and stack vectors inside unused entries of the application vector table. As an example, to compile for a Bluepill board with 128 KiB flash, use: make clean make TARGET=BLUEPILL_HIGH_128 @@ -64,7 +65,7 @@ You can also use the env variable `DEFS` to override default configuration for a ### Building for the bootloader The standard bootloader occupies the lower 8KiB of flash, so your application must offset its flash contents by 8KiB. This can be done by modifying your linker script or flags as appropriate. -The high memory bootloaders do not use the lower part of the flash, so you only need to make sure your application leaves 6.5kB of flash free. +The high memory bootloaders do not use the lower part of the flash, so you only need to make sure your application leaves 7 KiB of flash free. ### Switching to the bootloader diff --git a/src/stm32f103/stm32f103x8_high.ld b/src/stm32f103/stm32f103x8_high.ld index 25fda4e..9e9c124 100644 --- a/src/stm32f103/stm32f103x8_high.ld +++ b/src/stm32f103/stm32f103x8_high.ld @@ -23,7 +23,7 @@ MEMORY { vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 - rom (rx) : ORIGIN = 0x0800e600, LENGTH = 0x1A00 + rom (rx) : ORIGIN = 0x0800E400, LENGTH = 0x1C00 ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K } diff --git a/src/stm32f103/stm32f103xb_high.ld b/src/stm32f103/stm32f103xb_high.ld index 59d048d..df2a646 100644 --- a/src/stm32f103/stm32f103xb_high.ld +++ b/src/stm32f103/stm32f103xb_high.ld @@ -23,7 +23,7 @@ MEMORY { vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 - rom (rx) : ORIGIN = 0x0801e600, LENGTH = 0x1A00 + rom (rx) : ORIGIN = 0x0801E400, LENGTH = 0x1C00 ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K } diff --git a/src/stm32f103/stm32f103xc_high.ld b/src/stm32f103/stm32f103xc_high.ld index 7ca33f4..5c3ed66 100644 --- a/src/stm32f103/stm32f103xc_high.ld +++ b/src/stm32f103/stm32f103xc_high.ld @@ -23,7 +23,7 @@ MEMORY { vectors (rx) : ORIGIN = 0x08000000, LENGTH = 0x150 - rom (rx) : ORIGIN = 0x0803e600, LENGTH = 0x1A00 + rom (rx) : ORIGIN = 0x0803E400, LENGTH = 0x1C00 ram (rwx) : ORIGIN = 0x20000000, LENGTH = 48K } From 1cd9417ae1b58e9d9bf1bd78810855e0de39dc10 Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Sat, 8 Jul 2023 19:04:10 +0300 Subject: [PATCH 63/66] Disable SWD if PA13/PA14 are used for another purpose For example, the BTTSKRMINIE3V2 pinout uses SWDIO (PA13) and SWDCLK (PA14) for the STATUS LED and USB pullup respectively. Addressing those pins as GPIO requires disabling SWD. See https://github.com/Klipper3d/klipper/blob/6d48adf9ef5d17632acf53a7e3a07964f6cfd642/src/stm32/stm32f1.c#L152 for a similar application-side implementation. --- src/stm32f103/target_stm32f103.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/stm32f103/target_stm32f103.c b/src/stm32f103/target_stm32f103.c index 557f630..1965c0e 100644 --- a/src/stm32f103/target_stm32f103.c +++ b/src/stm32f103/target_stm32f103.c @@ -77,15 +77,26 @@ void target_clock_setup(void) { void target_gpio_setup(void) { /* Enable GPIO clocks */ - if (USES_GPIOA) { - rcc_periph_clock_enable(RCC_GPIOA); - } - if (USES_GPIOB) { - rcc_periph_clock_enable(RCC_GPIOB); - } - if (USES_GPIOC) { - rcc_periph_clock_enable(RCC_GPIOC); +#if USES_GPIOA + rcc_periph_clock_enable(RCC_GPIOA); +#endif +#if USES_GPIOB + rcc_periph_clock_enable(RCC_GPIOB); +#endif +#if USES_GPIOC + rcc_periph_clock_enable(RCC_GPIOC); +#endif + + /* Disable SWD if PA13 and/or PA14 are used for another purpose */ +#if ((HAVE_LED && LED_GPIO_PORT == GPIOA && (LED_GPIO_PORT == GPIO13 || LED_GPIO_PORT == GPIO14)) || \ + (HAVE_BUTTON && BUTTON_GPIO_PORT == GPIOA && (BUTTON_GPIO_PIN == GPIO13 || BUTTON_GPIO_PIN == GPIO14)) || \ + (HAVE_USB_PULLUP_CONTROL && USB_PULLUP_GPIO_PORT == GPIOA && \ + (USB_PULLUP_GPIO_PIN == GPIO13 || USB_PULLUP_GPIO_PIN == GPIO14))) + { + rcc_periph_clock_enable(RCC_AFIO); + gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, 0); } +#endif /* Setup LEDs */ #if HAVE_LED From 78ea3e665e1a66602bcbc00f6b9b79635f980e9d Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Sat, 15 Jul 2023 07:14:16 -0700 Subject: [PATCH 64/66] Replace CircleCI with GitHub Actions --- .circleci/config.yml | 25 ------------------ .github/workflows/build-firmware.yml | 38 ++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 25 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/build-firmware.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index a17319c..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: 2 -jobs: - build: - machine: true - steps: - - checkout - - run: - name: Checkout submodules - command: git submodule sync && git submodule update --init - - restore_cache: - key: v1-toolchain-checksum-{{ checksum "util/install-toolchain.sh" }} - - run: - name: Install toolchain - command: ./util/install-toolchain.sh - - save_cache: - key: v1-toolchain-checksum-{{ checksum "util/install-toolchain.sh" }} - paths: - - "~/toolchains/" - - run: - name: Compile firmware - command: make -f release.Makefile -k all - environment: - PREFIX: "~/toolchains/gcc-arm-embedded/bin/arm-none-eabi-" - - store_artifacts: - path: build/ diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml new file mode 100644 index 0000000..9e5e876 --- /dev/null +++ b/.github/workflows/build-firmware.yml @@ -0,0 +1,38 @@ +name: Build Firmware +on: [push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout source + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Cache Toolchain + id: cache-toolchain + uses: actions/cache@v3 + env: + cache-name: cache-toolchain + with: + path: ~/toolchains/ + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('util/install-toolchain.sh') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }} + name: Download toolchain + run: ./util/install-toolchain.sh + + - name: Build Firmware + run: make -f release.Makefile -k all + env: + PREFIX: ~/toolchains/gcc-arm-embedded/bin/arm-none-eabi- + + - name: Archive Firmware + uses: actions/upload-artifact@v3 + with: + name: firmware-bin + path: build/*.bin From 8f9011bc3208a50238f1fa31489ac401e3f4873e Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Thu, 20 Feb 2025 16:32:29 -0800 Subject: [PATCH 65/66] locm3: rebase to recent master The old version requires python2, which is no longer readily available --- libopencm3 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libopencm3 b/libopencm3 index b8b8d41..2c89ba3 160000 --- a/libopencm3 +++ b/libopencm3 @@ -1 +1 @@ -Subproject commit b8b8d416d4596d9b3de5962b800f0cbf138bf3b1 +Subproject commit 2c89ba385cb9a9ccedfaa26d05da1680ec5de22a From 8a1f099f6e43ce292eee3a326bd810b7e55f198f Mon Sep 17 00:00:00 2001 From: Devan Lai Date: Fri, 21 Feb 2025 20:26:09 -0800 Subject: [PATCH 66/66] github: update to upload-artifact v4 --- .github/workflows/build-firmware.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-firmware.yml b/.github/workflows/build-firmware.yml index 9e5e876..ee3a609 100644 --- a/.github/workflows/build-firmware.yml +++ b/.github/workflows/build-firmware.yml @@ -32,7 +32,7 @@ jobs: PREFIX: ~/toolchains/gcc-arm-embedded/bin/arm-none-eabi- - name: Archive Firmware - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: firmware-bin path: build/*.bin