Unable to use an external clock in IAP code

Helo, im starting to write a bootloader for the stm32f303 MCU (Im using a STM32F303 Discovery board for development) and I have some issue when trying to use the PLL with the HSE.

Here is the algorithm of the bootloader :

1. execute start bootloader (at address 0x8000000) :

- check RAM flag to know if we should start the application or the main bootloader

- start the main bootloader

2. execute the main bootloader (0x8001800) :

- blink LEDs on the board (simply to know that we entered the main bootloader

- set the RAM flag to request a start of the app

- do a system reset

3. execute start bootloader (at address 0x8000000) :

- at reset, the MCU executes again the instructions of the start bootloader

- check RAM flag to know if we should start the application or the main bootloader

- start the application

I want to make the MCU run as fast as it can, so I setup the PLL using rcc_clock_setup_pll (from libopencm3) to run at 72Mhz.

It works fine if I use the function at MCU start in the start bootloader.

But if instead I move it in the main bootloader (thus the clock tree is left as default when we enter the main bootloader) the execution stays stuck in rcc_clock_setup_pll.

Here is its code of rcc_clock_setup_pll :

void rcc_clock_setup_pll(const struct rcc_clock_scale *clock)
{
        if (clock->pllsrc== RCC_CFGR_PLLSRC_HSE_PREDIV) {
                rcc_osc_on(RCC_HSE);
                rcc_wait_for_osc_ready(RCC_HSE);
        } else {
                rcc_osc_on(RCC_HSI);
                rcc_wait_for_osc_ready(RCC_HSI);
        }
        rcc_osc_off(RCC_PLL);
        rcc_usb_prescale_1_5();
        if (clock->usbdiv1) {
                rcc_usb_prescale_1();
        }
        rcc_wait_for_osc_not_ready(RCC_PLL);
        rcc_set_pll_source(clock->pllsrc);
        rcc_set_pll_multiplier(clock->pllmul);
        rcc_set_prediv(clock->plldiv);
        /* Enable PLL oscillator and wait for it to stabilize. */
        rcc_osc_on(RCC_PLL);
        rcc_wait_for_osc_ready(RCC_PLL);
 
        /* Configure flash settings. */
        flash_prefetch_enable();
        flash_set_ws(clock->flash_waitstates);
 
        rcc_set_hpre(clock->hpre);
        rcc_set_ppre2(clock->ppre2);
        rcc_set_ppre1(clock->ppre1);
        /* Select PLL as SYSCLK source. */
        rcc_set_sysclk_source(RCC_CFGR_SW_PLL);
        /* Wait for PLL clock to be selected. */
        rcc_wait_for_sysclk_status(RCC_PLL);
 
        /* Set the peripheral clock frequencies used. */
        rcc_ahb_frequency  = clock->ahb_frequency;
        rcc_apb1_frequency = clock->apb1_frequency;
        rcc_apb2_frequency = clock->apb2_frequency;
}

After inspection with GDB it turns out the code loops indefinitely at two points :

- After turning on the PLL and waiting for it stabilize :  

rcc_wait_for_osc_ready(RCC_PLL);

- If I remove the line above the code now stays stuck at :

rcc_wait_for_sysclk_status(RCC_PLL); 

Removing the two wait calls resolves partially the problem : we exit the PLL setup but it is clearly not correctly configured (the code is executed extremely slowly). Replacing the two waites by a delay (1000000 nop) does not seems to help either. 

There is clearly something Im missing out with the PLL, why does it works when placed at the start of the start bootloader and not in the main bootloader?

While the only difference I can think of is the time between the power on of the MCU and the moment we call rcc_clock_setup_pll (time which is longer if we wait until the main bootloader to call the function). 

code of the start bootloader

#include <libopencm3/cm3/scb.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/f3/flash.h>
#include "../libroboot/boot.h"
#include "../libroboot/gpio.h"
#include "../libroboot/flash.h"
#include "../libroboot/utils.h"
#include "../libroboot/clock.h"

int main(void)
{

    uint32_t* boot_mode = (uint32_t*)BOOT_MODE_ADDR;
    if((*boot_mode) == BOOT_MODE_START_APP)
    {
        // Jump to application
        (*boot_mode) = BOOT_MODE_DEFAULT;
        uint32_t app_address = *((uint32_t *)(FLAGS_ADDR + APP_ADDR_FLAG_OFFSET * sizeof(uint32_t)));
        uint32_t app_offset = app_address - FLASH_START;
        start_application(app_address, app_offset);
    }
    else
    {
        // Jump to the main bootloader
        uint32_t main_btl_address = *((uint32_t *)(FLAGS_ADDR + MAIN_BTL_ADDR_FLAG_OFFSET * sizeof(uint32_t)));
        uint32_t main_btl_offset = main_btl_address - FLASH_START;
        start_application(main_btl_address, main_btl_offset);
    }
    while(1);
    return 0;
}

Code of the main bootloader
#include <libopencm3/cm3/scb.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/f3/flash.h>
#include "../libroboot/boot.h"
#include "../libroboot/gpio.h"
#include "../libroboot/flash.h"
#include "../libroboot/utils.h"
#include "../libroboot/clock.h"

static void delay(uint32_t count)
{
  uint32_t i;

  for (i = 0; i < count; i++)
    __asm__("nop");
}

static void blink_reverse(int count){
    /* Blink LEDs in reverse order */
    for (int i = 0; i < count; i++)
    {
        gpio_port_write(LEDS_PORT, (uint16_t) ~LED8_MASK);
        gpio_port_write(LEDS_PORT, LED1_MASK);
        delay(1000000);

        gpio_port_write(LEDS_PORT, (uint16_t) ~LED7_MASK);
        gpio_port_write(LEDS_PORT, LED8_MASK);
        delay(1000000);

        gpio_port_write(LEDS_PORT, (uint16_t) ~LED6_MASK);
        gpio_port_write(LEDS_PORT, LED7_MASK);
        delay(1000000);

        gpio_port_write(LEDS_PORT, (uint16_t) ~LED5_MASK);
        gpio_port_write(LEDS_PORT, LED6_MASK);
        delay(1000000);

        gpio_port_write(LEDS_PORT,(uint16_t) ~LED4_MASK);
        gpio_port_write(LEDS_PORT, LED5_MASK);
        delay(1000000);

        gpio_port_write(LEDS_PORT, (uint16_t) ~LED3_MASK);
        gpio_port_write(LEDS_PORT, LED4_MASK);
        delay(1000000);

        gpio_port_write(LEDS_PORT, (uint16_t) ~LED2_MASK);
        gpio_port_write(LEDS_PORT, LED3_MASK);
        delay(1000000);

        gpio_port_write(LEDS_PORT, (uint16_t) ~LED1_MASK);
        gpio_port_write(LEDS_PORT, LED2_MASK);
        delay(1000000);
    }
}

int main(void)
{
    rcc_clock_setup_pll(&rcc_hse8mhz_configs[RCC_CLOCK_HSE8_72MHZ]);

    /* Enable GPIO clocks. */
    rcc_periph_clock_enable(RCC_GPIOE);

    /* Setup I/O. */
    gpio_mode_setup(LEDS_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE,
                  LED1_MASK | LED2_MASK | LED3_MASK | LED4_MASK |
                  LED5_MASK | LED6_MASK | LED7_MASK | LED8_MASK);

    blink_reverse(1);

    // When all done reset the MCU to execute the app.
    volatile uint32_t* boot_mode = (uint32_t*)(BOOT_MODE_ADDR);
    (*boot_mode) = (uint32_t)BOOT_MODE_START_APP;
    asm volatile("DMB");
    scb_reset_system();
    while (1);

    return 0;
}

Code used to start the main bootloader from the start bootloader
void start_application(uint32_t application_vector_table_address, uint32_t offset)
{
    // Clear all pending interrupt requests in NVIC.
    cm_disable_interrupts();
    for (uint8_t i = 0; i < NVIC_IRQ_COUNT; i++)
    {
        nvic_clear_pending_irq(i);
    }

    // Load the vector table address into VTOR.
    SCB_VTOR = application_vector_table_address;
    asm volatile("DSB");

    // Set the MSP with the value from provided by the application vector table.
    asm volatile("MSR MSP, %0" ::"r"(*(uint32_t *)SCB_VTOR));
    asm volatile("ISB");

    // Set privileged access
    uint32_t control_value = 0;
    asm volatile("MSR CONTROL, %0" ::"r"(control_value));
    asm volatile("ISB");

    // Jump to application reset handler, will never return
    cm_enable_interrupts();
    uint32_t address = ((*(uint32_t *)(application_vector_table_address + 4)) + offset);
    ((void (*)(void))(address))();

    while(1);
}


TLDR : The setup of the PLL works fine at the start of the MCU but if I setup it instead after a jump to another program, it brakes and I don't know why.

Note : Im aware that the setup here would not work if I've already setup the clock tree to use the HSE and the PLL as source for SYSCLK but its not my case.
When I enter the main bootloader, the clock tree is in its default configuration not running on the PLL.

This is driving me crazy, any help is welcomed!
Parents Reply Children