cancel
Showing results for 
Search instead for 
Did you mean: 

stm32g431 setting up PLL and Flash wait state seems to have bricked my nucleos

Lvan .3
Associate II

While trying to get a bare metal environment up and running on the STM32G431 i seem to have found a way to brick both a nucleo-64 with an STM32G431RBT6 and a nucleo-32 with an STM32G431KBT6.

The first one , i switched on the HSE, waited for it to be ready, enabled PWR control, set VOS to 1 and cleared the CR5 boost mode,

i set the HPRE, PPRE1 and 2 divisors all to 1 (same as system clock, i could not find a reason in the datasheet not to.

then i went through the PLL config routine, setting M = 2 (divisor 3), N = 40, for what should end up being 320MHz VCO out, then PLLR divisor to 2 for a system frequency of 160Mhz. I wait for the PLLSYSRDY to come on, after which i set the PRFT, ICEN and DCEN bits in the FLASH ACR register, and finally i set the ACR latency to 5 (should be 5 wait states).

finally i do the set SW/wait till SWS confirms bit.

i can post the exact code later but i first want to describe the phenomenon:

the main program which blinks an LED did not come on, but whats more annoying, the ST-LINK built into the nucleo board doesnt recognize it anymore. i tried hooking up a known-good st-link to the swdio/swclk directly and i get the same behaviour: when i keep the reset button pressed, st-util --probe finds

serial:   303035303030323733303339353130393334333933383338

 hla-serial: "\x30\x30\x35\x30\x30\x30\x32\x37\x33\x30\x33\x39\x35\x31\x30\x39\x33\x34\x33\x39\x33\x38\x33\x38"

flash:   0 (pagesize: 2048)

 sram:    32768

 chipid:   0x0468

 descr:   G4 Category-2

if i don't keep the reset button pushed i get

Found 1 stlink programmers

 serial:   303035303030323733303339353130393334333933383338

 hla-serial: "\x30\x30\x35\x30\x30\x30\x32\x37\x33\x30\x33\x39\x35\x31\x30\x39\x33\x34\x33\x39\x33\x38\x33\x38"

 flash:   0 (pagesize: 0)

 sram:    0

 chipid:   0x0004

the interesting difference is that the flash size looks to be zero.

(thinking that this might be a limitation of the open source stutil, i tried installing STMCubeProg again, but like when i last tried a few years ago, the installation requires manual copying of java runtimes, and when you get it to run, it just crashes on these boards. it can connect to undamaged nucleos, but both of these broken boards make the STMCubeProg app crash when i click connect).

so having a nucleo32 lying around i proceeded a bit more careful: first i used the HSI instead of HSE to be sure i start with a working clock source. i then configured the PLL to come up to 72MHz and i set the HPRE, PPRE1 and PPRE2 to 2 so all peripherals were clocked at 36MHz.

Then i set the flash wait states to 5, like last time.

Interestingly, the main blinkyled program does come on, and it blinks at the expected frequency, but the ST-Link can't see the board anymore either.

trying to make the nucleo-48 boot into the boot0 bootloader so that it wouldnt even pass my ResetHandler is unsuccessful.

it seems that by setting the flash parameters i have made some irreversible changes. but i have not touched the option bytes. is that even possible? could it be that it always boots through my ResetHandler, messes up the FLASH access, so that the ST-Link finds an already misconfigured device and can't run anymore?

but then why can it read the chip ID?

i'm baffled and hesitant to brick my remaining nucleo board before i know what to look for.

12 REPLIES 12
TDK
Guru

It would be better to post your code rather than describe it. Increasing the clock before increasing flash wait states can certainly cause issues. If the clock initialization causes a hard fault, the st-link can lose connection. Connecting under reset, or booting into the bootloader instead can fix this.

If you feel a post has answered your question, please click "Accept as Solution".

> Interestingly, the main blinkyled program does come on, and it blinks at the expected frequency, but the ST-Link can't see the board anymore either.

Maybe it's not the clock, just incorrectly set GPIO so that the SWD pins are not set as required for SWD?

As TDK said:

> Connecting under reset, or booting into the bootloader instead can fix this.

JW

Lvan .3
Associate II

Thanks, and fair enough, i will cut it out to the essence and post here shortly.

However the more general point is: is it at all possible to irreversibly damage the chip, just by writing a program that changes clock and flash settings?

I would have expected booting into the boot0 bootloader or connecting under reset would be possible, but i don't seem to be able to do so.

Lvan .3
Associate II

this is called from the Reset_Handler:

static int setSysClockTo144MHz(void) {
 
	RCC.CR |= RCC_CR_HSEON;
 
	for (int i = 0; i < HSE_RDY_TIMEOUT; i++)
		if (RCC.CR & RCC_CR_HSERDY)
			break;
 
	if ((RCC.CR & RCC_CR_HSERDY) == 0)
		return 0; // HSE clock failed to become ready
 
	// Power control
	RCC.APB1ENR1 |= RCC_APB1ENR1_PWREN;
	pwr_cr1_set_vos(&PWR, 1);    // Voltage Scaling range 1 (fast)
	PWR.CR5 &= ~PWR_CR5_R1MODE;  // bit off = boost mode.
 
	// prepare periphperal clocks
	rcc_cfgr_set_hpre(&RCC, 8);  // AHB HCLK = SYSCLK / 2   =  72MHz
 
	while (rcc_cfgr_get_hpre(&RCC) != 8)
		__NOP();
 
	rcc_cfgr_set_ppre1(&RCC, 4); // APB1 PCLK = AHB HCLK / 2 = 36MHz
	rcc_cfgr_set_ppre2(&RCC, 4); // APB2 PCLK = AHB HCLK / 2 = 36MHz
 
	// Configure the main PLL 
	rcc_pllsyscfgr_set_pllsrc(&RCC, 3);      	   // select 2:HSI, 3:HSE as source (16/24MHz)
	rcc_pllsyscfgr_set_pllsysm(&RCC, 2);           // 0..15       : vco_in = HSE / (1+m)  2..16MHz                24/3 = 8MHz
	rcc_pllsyscfgr_set_pllsysn(&RCC, 36);          // 8...127     : vco_out = vco_in * n = 96...344MHz             8 * 36 = 288MHz
	rcc_pllsyscfgr_set_pllsysr(&RCC, 0); // 0,1,2,3 -> p=2,4,6,8  : sysclk = vco_out / p <= 170MHz                 8 * 36 / 2 = 144MHz
	RCC.PLLSYSCFGR |= RCC_PLLSYSCFGR_PLLSYSREN;
 
	RCC.CR |= RCC_CR_PLLSYSON;
 
	// Wait till PLL is ready
	while ((RCC.CR & RCC_CR_PLLSYSRDY) == 0)
		__NOP();
 
	// prepare the flash	
	FLASH.ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
	flash_acr_set_latency(&FLASH, 4);  // 5 wait states cpf p.192 table 29
 
	while(flash_acr_get_latency(&FLASH) != 4)
		__NOP();
 
	rcc_cfgr_set_sw(&RCC, 3); // Select PLL as system clock source
 
	// Wait till PLL is used as system clock source
	while (rcc_cfgr_get_sws(&RCC) != 3)
		__NOP();
 
	return 1;
}

To make sense of it you have to know that i generate the header, vectors.c and a link script include with constants and inline functions from the .svd,

the relevant parts are:

// from the linker script
FLASH	= 0x40022000;
PWR = 0x40007000;
RCC	= 0x40021000;

from stm32g431.h:

RCC_CR_HSERDY    = 1UL << 17, // HSE clock ready flag
RCC_CR_HSEON     = 1UL << 16, // HSE clock enable
 
RCC_APB1ENR1_PWREN    = 1UL << 28, // Power interface clock enable 
 
PWR_CR5_R1MODE = 1UL << 0, // Main regular range 1 mode
 
	RCC_CFGR_PPRE2  = ((1UL << 3) - 1) << 11, // APB high-speed prescaler (APB2)
	RCC_CFGR_PPRE1  = ((1UL << 3) - 1) << 8,  // PB low-speed prescaler (APB1)
	RCC_CFGR_HPRE   = ((1UL << 4) - 1) << 4,  // AHB prescaler
	RCC_CFGR_SWS    = ((1UL << 2) - 1) << 2,  // System clock switch status
	RCC_CFGR_SW     = ((1UL << 2) - 1) << 0,  // System clock switch
 
 
inline void rcc_cfgr_set_ppre2(struct RCC_Type* p, uint32_t val) { p->CFGR = (p->CFGR & ~RCC_CFGR_PPRE2) | ((val << 11) & RCC_CFGR_PPRE2); }
inline void rcc_cfgr_set_ppre1(struct RCC_Type* p, uint32_t val) { p->CFGR = (p->CFGR & ~RCC_CFGR_PPRE1) | ((val << 8) & RCC_CFGR_PPRE1); }
inline void rcc_cfgr_set_hpre(struct RCC_Type* p, uint32_t val) { p->CFGR = (p->CFGR & ~RCC_CFGR_HPRE) | ((val << 4) & RCC_CFGR_HPRE); }
inline void rcc_cfgr_set_sws(struct RCC_Type* p, uint32_t val) { p->CFGR = (p->CFGR & ~RCC_CFGR_SWS) | ((val << 2) & RCC_CFGR_SWS); }
inline void rcc_cfgr_set_sw(struct RCC_Type* p, uint32_t val) { p->CFGR = (p->CFGR & ~RCC_CFGR_SW) | ((val << 0) & RCC_CFGR_SW); }
 
inline uint32_t rcc_cfgr_get_sws(struct RCC_Type* p) { return (p->CFGR & RCC_CFGR_SWS) >> 2; }
inline uint32_t rcc_cfgr_get_sw(struct RCC_Type* p) { return (p->CFGR & RCC_CFGR_SW) >> 0; }
 
 
// RCC->PLLSYSCFGR PLL configuration register
enum {
	RCC_PLLSYSCFGR_PLLSYSPDIV = ((1UL << 5) - 1) << 27, // Main PLL division factor for PLLSAI2CLK
	RCC_PLLSYSCFGR_PLLSYSR    = ((1UL << 2) - 1) << 25, // Main PLL division factor for PLLCLK (system clock)
	RCC_PLLSYSCFGR_PLLSYSREN  = 1UL << 24,              // Main PLL PLLCLK output enable
	RCC_PLLSYSCFGR_PLLSYSQ    = ((1UL << 2) - 1) << 21, // Main PLL division factor for PLLUSB1CLK(48 MHz clock)
	RCC_PLLSYSCFGR_PLLSYSQEN  = 1UL << 20,              // Main PLL PLLUSB1CLK output enable
	RCC_PLLSYSCFGR_PLLSYSP    = 1UL << 17,              // Main PLL division factor for PLLSAI3CLK (SAI1 and SAI2 clock)
	RCC_PLLSYSCFGR_PLLPEN     = 1UL << 16,              // Main PLL PLLSAI3CLK output enable
	RCC_PLLSYSCFGR_PLLSYSN    = ((1UL << 7) - 1) << 8,  // Main PLL multiplication factor for VCO
	RCC_PLLSYSCFGR_PLLSYSM    = ((1UL << 4) - 1) << 4,  // Division factor for the main PLL and audio PLL (PLLSAI1 and PLLSAI2) input clock
	RCC_PLLSYSCFGR_PLLSRC     = ((1UL << 2) - 1) << 0,  // Main PLL, PLLSAI1 and PLLSAI2 entry clock source
};
inline void rcc_pllsyscfgr_set_pllsyspdiv(struct RCC_Type* p, uint32_t val) {
	p->PLLSYSCFGR = (p->PLLSYSCFGR & ~RCC_PLLSYSCFGR_PLLSYSPDIV) | ((val << 27) & RCC_PLLSYSCFGR_PLLSYSPDIV);
}
inline void rcc_pllsyscfgr_set_pllsysr(struct RCC_Type* p, uint32_t val) {
	p->PLLSYSCFGR = (p->PLLSYSCFGR & ~RCC_PLLSYSCFGR_PLLSYSR) | ((val << 25) & RCC_PLLSYSCFGR_PLLSYSR);
}
inline void rcc_pllsyscfgr_set_pllsysq(struct RCC_Type* p, uint32_t val) {
	p->PLLSYSCFGR = (p->PLLSYSCFGR & ~RCC_PLLSYSCFGR_PLLSYSQ) | ((val << 21) & RCC_PLLSYSCFGR_PLLSYSQ);
}
inline void rcc_pllsyscfgr_set_pllsysn(struct RCC_Type* p, uint32_t val) {
	p->PLLSYSCFGR = (p->PLLSYSCFGR & ~RCC_PLLSYSCFGR_PLLSYSN) | ((val << 8) & RCC_PLLSYSCFGR_PLLSYSN);
}
inline void rcc_pllsyscfgr_set_pllsysm(struct RCC_Type* p, uint32_t val) {
	p->PLLSYSCFGR = (p->PLLSYSCFGR & ~RCC_PLLSYSCFGR_PLLSYSM) | ((val << 4) & RCC_PLLSYSCFGR_PLLSYSM);
}
inline void rcc_pllsyscfgr_set_pllsrc(struct RCC_Type* p, uint32_t val) {
	p->PLLSYSCFGR = (p->PLLSYSCFGR & ~RCC_PLLSYSCFGR_PLLSRC) | ((val << 0) & RCC_PLLSYSCFGR_PLLSRC);
}
inline uint32_t rcc_pllsyscfgr_get_pllsyspdiv(struct RCC_Type* p) { return (p->PLLSYSCFGR & RCC_PLLSYSCFGR_PLLSYSPDIV) >> 27; }
inline uint32_t rcc_pllsyscfgr_get_pllsysr(struct RCC_Type* p) { return (p->PLLSYSCFGR & RCC_PLLSYSCFGR_PLLSYSR) >> 25; }
inline uint32_t rcc_pllsyscfgr_get_pllsysq(struct RCC_Type* p) { return (p->PLLSYSCFGR & RCC_PLLSYSCFGR_PLLSYSQ) >> 21; }
inline uint32_t rcc_pllsyscfgr_get_pllsysn(struct RCC_Type* p) { return (p->PLLSYSCFGR & RCC_PLLSYSCFGR_PLLSYSN) >> 8; }
inline uint32_t rcc_pllsyscfgr_get_pllsysm(struct RCC_Type* p) { return (p->PLLSYSCFGR & RCC_PLLSYSCFGR_PLLSYSM) >> 4; }
inline uint32_t rcc_pllsyscfgr_get_pllsrc(struct RCC_Type* p) { return (p->PLLSYSCFGR & RCC_PLLSYSCFGR_PLLSRC) >> 0; }
 
// FLASH->ACR Access control register
enum {
	FLASH_ACR_DBG_SWEN = 1UL << 18,             // Debug software enable
	FLASH_ACR_SLEEP_PD = 1UL << 14,             // Flash Power-down mode during Low-power sleep mode
	FLASH_ACR_RUN_PD   = 1UL << 13,             // Flash Power-down mode during Low-power run mode
	FLASH_ACR_DCRST    = 1UL << 12,             // Data cache reset
	FLASH_ACR_ICRST    = 1UL << 11,             // Instruction cache reset
	FLASH_ACR_DCEN     = 1UL << 10,             // Data cache enable
	FLASH_ACR_ICEN     = 1UL << 9,              // Instruction cache enable
	FLASH_ACR_PRFTEN   = 1UL << 8,              // Prefetch enable
	FLASH_ACR_LATENCY  = ((1UL << 4) - 1) << 0, // Latency
};
inline void flash_acr_set_latency(struct FLASH_Type* p, uint32_t val) {
	p->ACR = (p->ACR & ~FLASH_ACR_LATENCY) | ((val << 0) & FLASH_ACR_LATENCY);
}
inline uint32_t flash_acr_get_latency(struct FLASH_Type* p) { return (p->ACR & FLASH_ACR_LATENCY) >> 0; }

(if anyone is interested in the system i built to generate this, i may consider opensourcing it. it started from the misguided assumption the .SVD files would be ground truth, but i have had to fix quite some inaccuracies in them. the system parses the SVD xml and generates an API with no external dependencies, pretty straightforward to use and meeting our (aerospace grade) coding standards).

note that even if there's a bug in the above and i did mess up the PLL, my question is: how to force booting it into a sane state? boot0+reset did not help.

interesting hypothesis, but i only configure PB8 or PA5 to output to blink the led, nothing else.

also that doesnt explain why i can read the chip-id but not the flash size.

and note that i can only read that chip-id when actually under reset, otherwise nothing works. but even under reset the st-link program wont properly identify the flash size.

also the boot0 program does not seem to make the serial-bootloader protocol available on the USART1 pins

TDK
Guru

Not sure I want to sift through all the custom code. I can see why you described it instead of posting it in the original post. Seems like you're trying to recreate a lot of what's already in the CMSIS header include file. You have a lot of magic numbers in there.

As mentioned before, connecting under reset or booting into the bootloader can restore the chip, regardless of any bugs in your clock initialization. How the system boots into the bootloader is controlled by option bytes as well as the BOOTx pins. I would trust STM32CubeProgrammer over third party tools, especially on newer chip families.

If you feel a post has answered your question, please click "Accept as Solution".

Yeah i get that. (my reasons for generating my own linker scripts and header files can be discussed some other time. they have been well tested though, although the G431 in particular has not been cross checked with the CMSIS yet.)

The STM32CubeProgrammer dies on MacOs the moment i click 'connect', after the installer quit silently and needed manual java tweaking to get the thing installed at all, so thats exactly the kind of crapware i can not trust, but in this case i'll go find a linux machine to see if i can get further.

Meanwhile maybe can I turn the question around: does anyone have a snippet of code that succesfully puts a nucleo-64 board with G4 category 2 chip into 144MHz or higher?

Don't the NUCLEO's typically need the HSE BYPASS mode?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
does itt do irrepairable damage if i didn't?