STM32F3 DISCOVERY - PLL not behaving as expected in IAP code
Hello, Im writing a bootloader for the STM32F303 and I have some issue with the use of the HSE through the PLL using rcc_clock_setup_pll from libopencm3.
I have three pieces of software on the MCU, flashed using OpenOCD :
- A start bootloader flashed at 0x8000000 (role : select whether we start the main bootloader or the application)
- A main bootloader flashed at 0x8001800 (role : for now simply blink LEDs on the board to see that we entered it, set a flag in RAM to switch to the application, then do a system reset)
- An application flashed at 0x8010000 (simply blinky program started by the start bootloader after the system reset)
My issue :
- If I place the call rcc_clock_setup_pll(&rcc_hse8mhz_configs[RCC_CLOCK_HSE8_72MHZ]); at the start of the start bootloader (and never call it after in the main bootloader / application) it works fine
- If, instead, I place the call at the start of the main bootloader (while also removing the call from the start bootloader), we never exit the function.
After a check with GDB, I see the execution being stuck at two steps :
- After rcc_wait_for_osc_ready(RCC_PLL);
- And if I remove the line above, the execution is stuck at rcc_set_sysclk_source(RCC_CFGR_SW_PLL);
It seems like the PLLRDY flag never becomes true when we wait on it.
Removing the two call "solves" the issue (we exit the function) but the clock is not correctly configured (The MCU does not run on the PLL).
It seems weird to me because the only difference between the two scenarios (put rcc_clock_setup_pll in the start bootloader or in the main bootloader) is the time to reach the function call. Im doing nothing special in between, no clock configuration, no peripheral use,...
For context here is the code that cause problem :
Start bootloader
// From libopencm3
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;
}
static 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);
}
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;
}
Main bootloader
static void blink_reverse(int count){
/* 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 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]); // function call in which we stay stuck
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;
}
Is there missing checks / precautions in rcc_clock_setup_pll ?
