Divide by zero exception - 32F417 and Cube IDE - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2023, 07:25:25 am
content: I've noticed this config in Cube (ignore the yellowed line - I mean the one below it)What does it actually do? Is there some special CPU config, set in FLASH, which Cube sets up, via the debugger, before starting the program, and which determines this behaviour? I though the code I have is all the code that is runningI can test it of course and see what happens but what will that prove? Currently, an integer /0 generates a trap as expected. A floating /0 does not; you get a number which the debugger shows as "inf" and that is probably as per the IEEE floating spec.

author: wek
author_rank: Frequent Contributor
timestamp: May 19, 2023, 07:43:48 am
content: > I've noticed this config in CubeYou mean, in CubeIDE?Where do you see this window? To what functionality does it relate?What does the manual say?JW

author: darkspr1te
author_rank: Frequent Contributor
timestamp: May 19, 2023, 07:54:06 am
content: These options just change how it handles exceptions, (in cubemx they are in the file it stm32f4xx_it.c)in the bare bones case no handling is done and it hangs the cpu ,Code:/*** @brief This function handles Hard fault interrupt.*/void HardFault_Handler(void){/* USER CODE BEGIN HardFault_IRQn 0 *//* USER CODE END HardFault_IRQn 0 */while (1){/* USER CODE BEGIN W1_HardFault_IRQn 0 *//* USER CODE END W1_HardFault_IRQn 0 */}}however you can alter this behaviour, the example below dumps the stack trace to uart via putc/printfCode:/*** @brief This function handles Hard fault interrupt.*/void hard_fault_handler_c (unsigned int * hardfault_args){unsigned int stacked_r0;unsigned int stacked_r1;unsigned int stacked_r2;unsigned int stacked_r3;unsigned int stacked_r12;unsigned int stacked_lr;unsigned int stacked_pc;unsigned int stacked_psr;stacked_r0 = ((unsigned long) hardfault_args);stacked_r1 = ((unsigned long) hardfault_args);stacked_r2 = ((unsigned long) hardfault_args);stacked_r3 = ((unsigned long) hardfault_args);stacked_r12 = ((unsigned long) hardfault_args);stacked_lr = ((unsigned long) hardfault_args);stacked_pc = ((unsigned long) hardfault_args);stacked_psr = ((unsigned long) hardfault_args);printf ("\n\n[Hard fault handler - all numbers in hex]\r\n");printf ("R0 = %x\r\n", stacked_r0);printf ("R1 = %x\r\n", stacked_r1);printf ("R2 = %x\r\n", stacked_r2);printf ("R3 = %x\r\n", stacked_r3);printf ("R12 = %x\r\n", stacked_r12);printf ("LR  = %x subroutine call return address\r\n", stacked_lr);printf ("PC  = %x program counter\r\n", stacked_pc);printf ("PSR = %x\r\n", stacked_psr);printf ("BFAR = %x\r\n", (*((volatile unsigned long *)(0xE000ED38))));printf ("CFSR = %x\r\n", (*((volatile unsigned long *)(0xE000ED28))));printf ("HFSR = %x\r\n", (*((volatile unsigned long *)(0xE000ED2C))));printf ("DFSR = %x\r\n", (*((volatile unsigned long *)(0xE000ED30))));printf ("AFSR = %x\r\n", (*((volatile unsigned long *)(0xE000ED3C))));printf ("SCB_SHCSR = %x\r\n", SCB->SHCSR);while (1);}//intial save fault handler then branch to hard_fault_handler_c to dump to uart 1void HardFault_Handler(void){__asm volatile ("tst LR, #4 \n""ite EQ \n""mrseq R0,MSP \n""mrsne R0, PSP \n""b hard_fault_handler_c \n");__asm volatile (" movs r0,#4 \n"" movs r1, lr \n"" tst r0, r1 \n"" beq _MSP \n"" mrs r0, psp \n"" b _HALT \n""_MSP: \n"" mrs r0, msp \n""_HALT: \n"" ldr r1,[r0,#20] \n"" bkpt #0 \n");}so you can have cubemx handle exceptions when in debug mode and the ide will insert it's own code into these NMI's for debug and crash handling or you can provide your own or none at all.some IRQ exceptions are not system crash worthy example being uart/canbus/dma but you will see the code to handle that in the same place(file normaly).darkspr1te

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2023, 09:38:15 am
content: QuoteYes.QuoteIt is under Project / Debug As / Debug Configurations / Startup.QuoteI don't know. It could be just relating to what the debugger does when you step through a /0.I would expect that, if say a div/0 trap can be enabled or not by some CPU config register, there would be a CPU config register bit somewhere for this.QuoteRight, but where is this configured?More to the point, I would be surprised if a checkbox in Cube IDE was causing some .c source file to be modified. Never seen that. This is not Cube MX (the code generator, which I've never used).QuoteYou mean the RM for the 32F417? I can't find anything in there relating to whether the CPU traps or not.I did some digging and found this bywekon the ST forumQuoteSo I went looking through my startup codeCode:// The following initialisation code is an inline condensation of what was// in various functions which were called in a sequence. The sequence has been// preserved.// Some of the initialisation was duplicated via messy coding; this has been removed.// Set 5 wait states *before* selecting the 168MHz speed below. This setting is// duplicated below. ACR resets to 0, so we just load this value straight in.//FLASH->ACR = FLASH_ACR_LATENCY_5WS;// ========== This was in SystemInit() ============#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */#endif/* Reset the RCC clock configuration to the default reset state ------------*//* Set HSION bit */RCC->CR |= (uint32_t)0x00000001;/* Reset CFGR register */RCC->CFGR = 0x00000000;/* Reset HSEON, CSSON and PLLON bits */RCC->CR &= (uint32_t)0xFEF6FFFF;/* Reset PLLCFGR register */RCC->PLLCFGR = 0x24003010;/* Reset HSEBYP bit */RCC->CR &= (uint32_t)0xFFFBFFFF;/* Disable all clock interrupts */RCC->CIR = 0x00000000;// ============ This was in SetSysClock() ============// The settings were partly overriden later by stuff in SystemClock_Config()!// Configure the System clock source, PLL Multiplier and Divider factors,// AHB/APBx prescalers and Flash settingsvolatile uint32_t StartUpCounter = 0, HSEStatus = 0;/* Enable HSE */RCC->CR |= ((uint32_t)RCC_CR_HSEON);/* Wait till HSE is ready and if Time out is reached exit */do{HSEStatus = RCC->CR & RCC_CR_HSERDY;StartUpCounter++;} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));/* Enable high performance mode, System frequency up to 168 MHz */RCC->APB1ENR |= RCC_APB1ENR_PWREN;PWR->CR |= PWR_CR_PMODE;/* HCLK = SYSCLK / 1*/RCC->CFGR |= RCC_CFGR_HPRE_DIV1;/* PCLK2 = HCLK / 4 i.e. 42MHz */RCC->CFGR |= RCC_CFGR_PPRE2_DIV4;/* PCLK1 = HCLK / 4 i.e. 42MHz */RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;// PLL Parameters// These produce a 168MHz CPU clock (336/2) and a 48MHz USB clock (336/7)/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */#define PLL_M 25#define PLL_N 336/* SYSCLK = PLL_VCO / PLL_P */#define PLL_P 2/* USB OTG FS, SDIO and RNG Clock = PLL_VCO / PLLQ */#define PLL_Q 7/* Configure the main PLL */RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);/* Enable the main PLL */RCC->CR |= RCC_CR_PLLON;/* Wait till the main PLL is ready */while((RCC->CR & RCC_CR_PLLRDY) == 0) {}/* Configure Flash prefetch, Instruction cache, Data cache and wait state */FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;/* Select the main PLL as system clock source */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= RCC_CFGR_SW_PLL;/* Wait till the main PLL is used as system clock source */while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL) {}// This bit was in SystemClock_Config(), with duff comments// Enable peripheral clocks, and wait for them to settle__HAL_RCC_PWR_CLK_ENABLE();// Scale 1 mode (default value at reset): allows the maximum value of fHCLK = 168 MHz// If this was loading a different value, it probably wants to be much earlier!__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);// Configure the Vector Table location. No change from the reset value.SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET_BOOT;// set VT location// Enable CYCCNT as a free running counter, for various timeouts where we// don't want to rely on interrupts.// Runs at B_SystemCoreClock (or SystemCoreClock - both same) HzCoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;DWT->CYCCNT = 0;// start it at zeroDWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;// Stop IWDG if single stepping__HAL_DBGMCU_FREEZE_IWDG();// Initialise I/O clocks. Without this one cannot set up any I/O pins etc.// Also sets up C9 for 25MHz outputB_HAL_RCC_MCOConfig(RCC_MCO2, RCC_MCO2SOURCE_HSE, RCC_MCODIV_1);A trawl through the countless registers in the RM didn't find anything with a bit in it which might control this trap. But there are other manuals e.g. a huge one for the ARM32 CPU alone (by ARM, not ST).Then I found thishttps://www.keil.com/appnotes/files/apnt209.pdfwhich describes a "CCR" register but the RM contains many of these, relating to peripherals. Also my C sourcecode does not have a "CCR" so it is called something else. Searching the RM for e.g. "hard fault" find nothing.QuoteYes; that hard fault trap is taken on a div/0, currently. That works as it should. But where is it configured, is the Q.The answer is probably very simple but I can't find the config registerLooking at that Keil PDF, it describes it here as SCB -> CCR but I can't find it in the RMIt looks like I am not writing SCB -> CCR anywhere. Also I can't see such code in any ST "HAL" stuff (which I am not using anyway in this case).

author: abyrvalg
author_rank: Frequent Contributor
timestamp: May 19, 2023, 09:52:01 am
content: Div by 0 exception is not an ST-specific feature (quite obviously), so the answer is in ARM’s Cortex-M4 manual. A quick search finds this:https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/system-control-block/configuration-and-control-register?lang=en(that’s for M3, but the reg is the same, SCB->CCR).Edit: your edit was fasterThe problem with ST’s manual is that it explains some of the CM core regs (0xE000xxxx addresses) and it makes feeling like they are covered there, but the info is incomplete indeed, ARM’s docs says more.

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2023, 09:58:50 am
content: Right, thanks. Edits crossed a little.If SCB -> CCR defaults to 0, the /0 should not trap, but it does.Hence I wonder if Cube IDE is somehow configuring that, but how can it, other than by an explicit write to CCR. And there isn't such a write in my code.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: May 19, 2023, 10:05:02 am
content: Yes, the menu location and other options like “Break at main” near it suggests it is not a code generation flag, but debugger configuration: the IDE writes to CCR at debug session init (directly, by sending a mem write cmd to the debugger dongle). You can try running the same code w/o debugger attached and it shouldn’t trap divs by 0 in this case.

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2023, 10:34:00 am
content: You are damn right. Nasty nasty nasty one! Been working on this for 3 years and only just found this.With the debugger connected but with its USB cable unplugged, the /0 trap isnottaken.With the USB cable connected afterwards, it is still not taken, but that is now obvious since Cube found the debugger missing earlier and gave up on it.If I load the code via the debugger (F11) then it traps.So I added, right at the start of my C codeCode:// This is the default value of CCR. Cube IDE loads potentially different values// according to debugger config// https://www.eevblog.com/forum/microcontrollers/divide-by-zero-exception-32f417-and-cube-ide/// and this overrides it. A value of 0 disables the /0 hard fault trap, for example.SCB->CCR=0;I wonder what else might be in there? What else might the debugger be setting up? Perhaps one needs to look at any checkboxes relating to the debugger.Interesting that Cube IDE (MX) does not generate this code, so people will never know.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: May 19, 2023, 10:52:08 am
content: Why do you need to clear the CCR explicitly? Just disable Cube features you don’t want instead?The reset value is 0 anyway, but you can turn on the trapping temporary when debugging to check for /0, then turn it off. Having this CCR=0 in the startup code together with trapping enabled in Cube can result in surprise behavior when you perform an “attach to already running code” - the debugger will write it’s own setting and your code would not have a chance to clear it (the clear is in the startup).

author: wek
author_rank: Frequent Contributor
timestamp: May 19, 2023, 10:54:37 am
content: > With the debugger connected but with its USB cable unplugged, the /0 trap is not taken.So this is exactly what the tickbox in initial post means. As this is> It is under Project / Debug As /Debug Configurations / Startup.that means, when CubeIDE starts debugging session, it sets a couple of things which it thinks may be useful during debugging - and in this window it enables you to allow or disallow the debugger to do these things when it starts up.As I wrote, these are the automagic obscurities of debuggers integrated in IDEs, which the various IDEs like to enable by default for "better user experience". Users want to click, not to wade through config files, not even documentation. That's why I asked>> What does the manual say?by which I of course meant the CubeIDE manual, as you were asking about a CubeIDE feature.JW

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2023, 11:08:19 am
content: QuoteAre you saying that the Cube /0 trap could be re-enabled if I stop the program (say at a breakpoint) and then restart it? That does not appear to happen because this code runs just fine after the printf, and z=0 as per the manualI wonder if there are other such features in Cube?QuoteI've never seen anything readable for Cube.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: May 19, 2023, 11:24:01 am
content: > Are you saying that the Cube /0 trap could be re-enabled?No, I mean a different debug mode where you don’t start the code under debugger from the beginning, but attach to an already running device. I.e. you have the device working standalone, notice something abnormal, plug the debugger in and “attach” w/o restarting the CPU to observe the current state. Not sure what Cube would do with it’s debug settings in this case, perhaps it would apply them.

author: gf
author_rank: Super Contributor
timestamp: May 19, 2023, 12:24:08 pm
content: Quote from: peter-h on May 19, 2023, 11:08:19 amCheck the assembly code emitted by the compiler. I think gcc does not emit a divide instruction, but directly a call to abort() if x and y are known at compile time and y is known to be zero.

author: wek
author_rank: Frequent Contributor
timestamp: May 19, 2023, 12:41:58 pm
content: >> by which I of course meant the CubeIDE manual, as you were asking about a CubeIDE feature.> I've never seen anything readable for Cube.Okay so I looked it up. CubeIDE manual is UM2609, and the feature in question is described in subchapter 3.2.4 Startup tab. It starts with:The Startup tab configureshow to start a debug session.(Emphasis mine).> Are you saying that the Cube /0 trap could be re-enabled if I stop the program (say at a breakpoint) and then restart it? Step/continue after breakpoint/pause do not constitute astart of debug session, they are operations within one debug session.> I think gcc does not emit a divide instruction, but directly a call to abort() if x and y are known at compile time and y is known to be zero.That depends on... several things, among others, optimization level. Nevertheless,> Check the assembly code emitted by the compiler.is the way to go in any case.JW

author: AVI-crak
author_rank: Regular Contributor
timestamp: May 19, 2023, 01:57:41 pm
content: Code:uint32_t mmmm = 0;NVIC_EnableIRQ(FPU_IRQn);void FPU_IRQHandler (void){//your error handling codemmmm++;}

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2023, 03:12:47 pm
content: What is the above for?QuoteI find it unbelievable that the above is at all possible, without crashing the CPU.It would be useful for sure (because the debugger never remains connected for more than some hours) but IME all one needs to do is reconnect the debugger USB cable and the target restarts... and I am using a galvanically isolated debugger (STLINK V3 ISOL).A good find,wek- thanksFirstly, it doesn't actually say that the debugger executes code which sets up some SCB->CCR bits. I guess it is obvious to those who already know how this fiendishly complicated stuff works.Secondly, the last sentence is false; e.g. the value SCB_CCR_DIV_0_TRP is not in that .h file or anywhere else in the whole project.Thankfully, the last para does suggest that the debugger is not messing with any other CPU config bits; just those two, in SCB->CCR.I sort of expected the CPU to have some feature whereby some pins are sampled at startup, to load SCB->CCR, but apparently not.I am disabling the /0 trap because in a typical embedded system there is no way to handle it. All one could do is save some data somewhere and let the watchdog restart it. I've documented this; if somebody is dividing by zero they will get garbage. And with floats there is no trap anyway.QuoteFirstly, I actually used "volatile int z" otherwise that whole bit of code gets optimised away (using -Og). Secondly, I find that z=0 after executing the above, which suggests the div is happening, because z is some huge value beforehand (as expected, since not being =0 it doesn't end up in BSS). You are right; I will check the asm and report if anything strange.

author: wek
author_rank: Frequent Contributor
timestamp: May 19, 2023, 04:14:49 pm
content: > And with floats there is no trap anyway.Who said that?See the FPU IRQ in NVIC (position 81) and 4.6.7 Enabling and clearing FPU exception interrupts in PM0214.The problem in 'F4 is, that it does not have implemented masks for individual FPU interrupt sources, so you have to distinguish them "manually" (see e.g. the 'G4 RM, SYSCFG_CFGR1.FPU_IE). And, as the text above explains, clearing them is not that simple either.JW

author: peter-h
author_rank: Super Contributor
timestamp: May 19, 2023, 05:01:54 pm
content: One lives and learnsThank you all.The DIV is being done for real

author: abyrvalg
author_rank: Frequent Contributor
timestamp: May 19, 2023, 10:14:46 pm
content: > I find it unbelievable that the above is at all possible, without crashing the CPUSure, why not? It appears to be a bit more complicated in Cube (the debugger I’m more accustomed to provides both “Start” and “Attach” right in run control menu), but nothing impossible:https://www.kzeise.com/2021/attaching-to-a-running-target-with-stm32cubeide-debugger/You create a separate Debug config without Download and Reset steps, so it doesn’t disturb the CPU on attach.

author: peter-h
author_rank: Super Contributor
timestamp: May 20, 2023, 07:08:47 am
content: Interesting link.However I think you would struggle live-attaching anisolateddebugger to a target, because the isolated circuit needs to be target-powered (unless there is a transformer, and AFAIK no debugger has got that) and all you need is a 100nF decoupling capacitor VCC-GND on the isolated side, and you will crash the target.

author: wek
author_rank: Frequent Contributor
timestamp: May 20, 2023, 08:55:08 am
content: Quote from: peter-h on May 20, 2023, 07:08:47 amEverything non-trivial is struggle... :-)Quote from: peter-h on May 20, 2023, 07:08:47 amYou looked?Quote from: peter-h on May 20, 2023, 07:08:47 amJust use a better power supply which won't drop when you attach a 100nF decoupling capacitor. Or add a series resistor. Or decouple on USB side. Or use isolated PC.There are also other potential sources of reset - for example, the reset line, if you connect that too.JW

author: peter-h
author_rank: Super Contributor
timestamp: May 20, 2023, 09:25:07 am
content: I have bought a pile of debuggers and taken them apart, yes.It could be done. I sell a load of products which use an inverter to power the "remote" end (isolated interface converters). It costs a little bit of money... but can easily be USB-powered. I've got the power draw down to 2mA.Interesting angle though, not least because the ST debuggers seem to always lose connection after a while.