STM32F103C8T6 ARM STM32 Minimum System Development Board

September 5, 2020

libmaple -- trouble flashing the board

In the examples directory there is "blinky.cpp" which looks to be as simple as one could hope for. I copy it to main.cpp and build it.

After a successful build, I decided to try flashing the image. I used "make jtag" then with openocd and my STlink V2 I flashed the image just as I always have done with my own bare metal code.

The download complained after verifying that the reset had failed. And nothing seems to be running. No lights are blinking. And I cannot flash more code, at least not without moving jumpers.

My device was easily restored by setting the BOOT0 jumper to 1 so that it was forced to run the boot loader in system memory. This allowed me to load my blink demo and now the device is back to normal.

My guess is whatever code had been loaded either had the device in a tight loop or caused some exception to lock it up. (This turns out to be not true. The device is running fine, but is blinking and LED on a wrong pin. Why I cannot flash again without moving jumpers is an unsolved problem).

Details on the blinky build

Here is the tail end of the output when I set V=1.
arm-none-eabi-g++ -Os -g3 -gdwarf-2 -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections  -mcpu=cortex-m3 -march=armv7-m -mthumb -DBOARD_maple_mini -DMCU_STM32F103CB -DERROR_LED_PORT=GPIOB -DERROR_LED_PIN=1 -DVECT_TAB_FLASH -I./libmaple/include/libmaple -I./wirish/include/wirish -I./libraries  -DBOOTLOADER_maple  -fno-rtti -fno-exceptions -Wall  -mcpu=cortex-m3 -march=armv7-m -mthumb -DBOARD_maple_mini -DMCU_STM32F103CB -DERROR_LED_PORT=GPIOB -DERROR_LED_PIN=1 -DVECT_TAB_FLASH -I./libmaple/include/libmaple -I./wirish/include/wirish -I./libraries   -I./libmaple/include -I./libmaple/stm32f1/include -I./wirish/include -I./wirish/boards/maple_mini/include -o build/main.o -c main.cpp
arm-none-eabi-g++  -Xlinker -T./support/ld/flash.ld -L ./support/ld/stm32/series/stm32f1/performance -L ./support/ld/stm32/mem/sram_20k_flash_128k -L ./support/ld -L ./support/ld/toolchains/generic -mcpu=cortex-m3 -mthumb -Xlinker --gc-sections -Xassembler --march=armv7-m -Wall -o build/maple_mini.elf  build/./libmaple/adc.o build/./libmaple/dac.o build/./libmaple/dma.o build/./libmaple/exti.o build/./libmaple/flash.o build/./libmaple/gpio.o build/./libmaple/iwdg.o build/./libmaple/nvic.o build/./libmaple/pwr.o build/./libmaple/rcc.o build/./libmaple/spi.o build/./libmaple/systick.o build/./libmaple/timer.o build/./libmaple/usart.o build/./libmaple/usart_private.o build/./libmaple/util.o build/./libmaple/i2c.o build/./libmaple/exc.o  build/./libmaple/usb/stm32f1/usb.o build/./libmaple/usb/stm32f1/usb_reg_map.o build/./libmaple/usb/stm32f1/usb_cdcacm.o build/./libmaple/usb/usb_lib/usb_core.o build/./libmaple/usb/usb_lib/usb_init.o build/./libmaple/usb/usb_lib/usb_mem.o build/./libmaple/usb/usb_lib/usb_regs.o build/./libmaple/stm32f1/performance/isrs.o build/./libmaple/stm32f1/performance/vector_table.o build/./libmaple/stm32f1/adc.o build/./libmaple/stm32f1/bkp.o build/./libmaple/stm32f1/dma.o build/./libmaple/stm32f1/exti.o build/./libmaple/stm32f1/fsmc.o build/./libmaple/stm32f1/gpio.o build/./libmaple/stm32f1/i2c.o build/./libmaple/stm32f1/rcc.o build/./libmaple/stm32f1/spi.o build/./libmaple/stm32f1/timer.o build/./libmaple/stm32f1/usart.o build/./wirish/start.o build/./wirish/start_c.o build/./wirish/syscalls.o build/./wirish/stm32f1/util_hooks.o build/./wirish/boards.o build/./wirish/cxxabi-compat.o build/./wirish/ext_interrupts.o build/./wirish/HardwareSerial.o build/./wirish/HardwareTimer.o build/./wirish/Print.o build/./wirish/pwm.o build/./wirish/usb_serial.o build/./wirish/HardwareSPI.o build/./wirish/wirish_analog.o build/./wirish/wirish_digital.o build/./wirish/wirish_math.o build/./wirish/wirish_shift.o build/./wirish/wirish_time.o build/./wirish/stm32f1/boards_setup.o build/./wirish/stm32f1/wirish_digital.o build/./wirish/stm32f1/wirish_debug.o build/./wirish/boards/maple_mini/board.o  build/./libraries/Servo/Servo.o  build/./libraries/LiquidCrystal/LiquidCrystal.o  build/./libraries/Wire/WireBase.o build/./libraries/Wire/HardWire.o build/./libraries/Wire/Wire.o build/main.o -Wl,-Map,build/maple_mini.map
arm-none-eabi-objcopy -v -Obinary build/maple_mini.elf build/maple_mini.bin 1>/dev/null
arm-none-eabi-objdump -d build/maple_mini.elf > build/maple_mini.disas
The resulting "bin" file is 15140 bytes, which isn't as frightening as the 731192 bytes in the elf file.

A look at maple_mini.disas shows the text section beginning at 0x08005000, with the whole thing starting off with a vector table. This would explain why just shoving the thing in over the SWD port and expecting it to just run isn't working out. I see this:

08005000 <__stm32_vector_table>:
 8005000:       00 50 00 20 49 73 00 08 91 5b 00 08 95 5b 00 08     .P. Is...[...[..
 8005010:       99 5b 00 08 9d 5b 00 08 a1 5b 00 08 2b 6d 00 08     .[...[...[..+m..

The source that yields the above is in "libmaple/libmaple/stm32f1/performance/vector_table.S" and is as follows:

       .section        ".stm32.interrupt_vector"

        .globl  __stm32_vector_table
        .type   __stm32_vector_table, %object

__stm32_vector_table:
        .long   __msp_init
        .long   __exc_reset
        .long   __exc_nmi
But if you build with "make jtag", everything ends up at 0x08000000 as it should, so hmmm.

One of my working projects starts out like this at address 0x08000000

.word   0x20005000  /* stack top address */
.word   _reset      /* 1 Reset */

.thumb_func
_reset:
    bl startup
    b .
So I try the jtag build again and get the same failure. It programs 15360 bytes (which is 30*512) and then verifies 15140 bytes OK. 15140 is the size of the bin file, which seems just right. It fails when resetting the target and doesn't give a complete error, only:
** Resetting Target **
in procedure 'program'
in procedure 'reset' called at file "embedded:startup.tcl", line 540
in procedure 'ocd_bouncer'
In any case though, resetting the board doesn't yield proper behavior either, so the problem does not lie with openocd despite its incomplete message.

Trial and error debugging

I am a little surprised that I am debugging the libmaple setup, but so it seems to be. First I look at the path to start execution. The vector table is the starting place and the symbol __exc_start gets defined in the linker script to be __startup__ which is in wirish/start.S. The "wirish" directory is a collection of C++ code that I hoped I would avoid, but so be it. The code in start.S calls start_c() which is in wirish/start_c.c - this will do a bunch of proper initialization and then call main(), but there is some trickery with something called premain() to sort out.

I copy my blink1 demo, which is 100 lines of self contained C code that will blink the PC13 LED (which is a red LED on my board). I make sure everything in here is static, except for "tom_startup()" and append this code to start_c.c I put a call to tom_startup at the head of start_c(), compile the mess and load it into my device. It runs and blinks the LED!!

Oddly though, I cannot just reflash again and again using the STLINK as I can with my own demos, but this is progress of a sort. Being able to flash once is a big step forward.

Now I move my call to just before the call to main(). I get an error from openocd about resetting the chip, and now the LED is blinking very fast. I am also unable to connect to the target. So this is all the same as the original problem, but now with the LED flashing like crazy. I move my call to just before "__libc_init_array()" and we are back to blinking at a normal rate.

The routine "libc_init_array()" is supplied as part of the compiler toolchain. It supports C++ constructors and the ".init_array". There is no reason for me to dig any deeper into these details for now.

Take stock of what we have learned

With my blink code added, I am able to cleanly download and run an image. In fact, I can do this over and over (with the blink sitting before libc_init_array() ). The odd problem with the reset at the end of the download has now gone away.

The rapid flashing makes sense. My simple blink demo did not set the PLL to bump the processor speed up to 72 Mhz, and apparently once the stuff triggered by libc_init_array() runs, libmaple has bumped up the CPU speed.

Board LED pin

I am experimenting with the blinky.cpp example. It claims to blink the onboard LED. The code manipulates "BOARD_LED_PIN", which is different for each board. For the maple-mini this is D33, which is PB1. For the blue pill, this is D14, which is PC13 (near as I can tell).
So I hack blinky.cpp to override the definition.

And indeed this works.

Conclusion

First - we learned that the Blue Pill and Maple Mini used a different IO pin for the onboard LED.

Second - we have an unsolved and mildly inconvenient mystery in flashing the maple software into the blue pill. We have to move the BOOT0 jumper, reset to get the in-chip boot loader to run, then we can flash our software. Then we move the jumper back and reset again to run the software we just flashed.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org