2023-07-13 12:02 AM
Hi all,
I am currently experiencing some weird reset behavior for my STM32L47 chip. Due to a non-optimal combination of HW the chip resets from time to time, and I am assuming an ESD issue being the reason for this. As long as the resets are recoverable, it does not really matter.
This is where the trouble arises: Some of the resets are not recoverable, and the chip get stuck in a weird state and will not power on correctly. We see that the watchdog triggers, but it does not enter its mainloop. If you spam the pin-reset it can in some cases reset correctly, but pressing it once does not get it out of this freezed state. So the easiest way to get out of this state is to cut the power and turn it on again. Checking the control status registers during resets we have found the following:
So the common denominator for resets that causes the chip to freeze is brownout. If this indicates an actual brownout, or simply a "bug" due to some strange ESD behavior I can't really tell. We have tried measuring during resets as well, but we have not found any obvious bugs in the HW. We have also simulated a brownout, but did not manage to get it into the freezed state.
We tried to increase the brownout level from 1.7 V (default) to 2.5 V, but this had no effect and we still experienced the "freezed" behavior.
Does anyone have any tips on what this freezed behavior can be caused by? Is there some obvious configuration to look into when experience these types of bugs? FYI: the chip are currently running RUST code.
2023-07-13 12:12 AM
Spurious bootloader (system-memory) entry? BOOT0 pin is wired how exactly?
JW
2023-07-14 05:12 AM
I am a collegue of @hlanden
The BOOT0 pin is pulled low with a 10kΩ,
There are indication that the system is stuck at clock init and we are also calling a couple of statics (buffers) before WD is initialized but not sure if this has any implications.
2023-07-14 05:48 AM
> There are indication that the system is stuck at clock init
What indication? The usual thing to do when something like this is suspected is to toggle a pin around the suspected routine and observe on oscilloscope (or just watch if it's a LED).
What is the clock? How is it used? Is there any LSE clock/VBAT? You may perhaps want to post relevant portion of schematics.
JW
2023-07-14 07:17 AM - edited 2023-07-14 07:28 AM
Hi again ;)
Attaching clock configuration, can provide more info if needed. I also added the init of the periphs...
Added the schematics for the clock HSE and LSE, and power inputs.
We tested changing the IWDG timeout to random number eg. 6-10 sec. Then we observed the NRST changing accoringly but the system was still in a "Lockout" mode. Only a full power cycle would start it normally again.
Connecting a debugger unstucks the MCU.
// clock configuration (clocks run at nearly the maximum frequency)
let clocks = rcc
.cfgr
.lse(CrystalBypass::Disable, ClockSecuritySystem::Disable)
.msi(MsiFreq::RANGE4M)
.sysclk(FREQ_HZ.hz())
.pclk1(FREQ_HZ.hz())
.pclk2(FREQ_HZ.hz())
.freeze(&mut flash_parts.acr, &mut pwr);
// clock configuration using HSE external high speed crystal
/*
let clocks = rcc
.cfgr
.hse(
20_000_000.hz(),
CrystalBypass::Disable,
ClockSecuritySystem::Disable,
)
.pll_source(PllSource::HSE)
.sysclk_with_pll(FREQ_HZ.hz(), PllConfig::new(2, 16, PllDivider::Div2))
.pclk1(FREQ_HZ.hz())
.pclk2(FREQ_HZ.hz())
.freeze(&mut flash_parts.acr, &mut pwr);
*/
delay_ms!(clocks, 400);
green_led.set_low().unwrap();
pub fn init_peripherals(
mut dev: hal::stm32::Peripherals,
uart5_dma_rx_buffer: &'static mut imu_config::Uart5DmaRxBuffer,
uart4_dma_rx_buffer: &'static mut lidar_config::Uart4DmaRxBuffer,
) -> (
imu_config::Uart5DmaRxContext,
hal::serial::Rx<hal::pac::UART5>,
can_lib::Can,
IndependentWatchdog,
GreenLedPin,
RedLedPin,
BlueLedPin,
YellowLedPin,
BuzzerPwm,
LidarTx,
LidarRx,
lidar_config::Uart4DmaRxContext,
power_gates::PowerGatesControl,
BmsSet,
Ws2812,
ImuRstPin,
NfcBindings,
WcanSenders,
Tms320Serial,
SmcuInterface,
hal::flash::Parts,
AdcChannels,
) {
// Enable watchdog ASAP
let mut watchdog = IndependentWatchdog::new(dev.IWDG);
watchdog.stop_on_debug(&dev.DBGMCU, true);
watchdog.start(MilliSeconds(10000));
// Not sure who took them, so we try to steal them instad
let mut cp = unsafe { cortex_m::peripheral::Peripherals::steal() };
// Re-allocate interrupt vector table to 0x0800_0000
// This has to be done so code can run from both Bank1 and Bank2
unsafe {
cp.SCB.vtor.write(0x0800_0000);
}
let mut flash_parts = dev.FLASH.constrain();
let rcc_reg = dev.RCC;
rcc_reg.apb1enr1.modify(|_, w| w.can1en().set_bit());
let mut rcc = rcc_reg.constrain();
// Dubious enabling of the DMA clocking
unsafe {
let rcc_ptr = &*hal::pac::RCC::ptr(); // RCC pointer
// enable DMA1 clock: peripheral clock enable register
rcc_ptr.ahb1enr.modify(|_, w| w.dma1en().set_bit());
}
let dma1 = dev.DMA1;
dma1.cselr.write(|w| w.c5s().bits(1));
let dma1_channels = dma1.split(&mut rcc.ahb1);
let _dma2_channels = dev.DMA2.split(&mut rcc.ahb1);
2023-07-14 11:48 PM
> Then we observed the NRST changing accoringly but the system was still in a "Lockout" mode
What do you mean by "Lockout" mode? Is it repeated watchdog reset (i.e. NRST resetting periodically)?
Have you tried to spot the exact point where the problem occurs, e.g. by the "toggle a pin/LED around suspicious pieces of code" method? I don't Rust but the piece of code you've posted appears to be pretty high level, so if you are using some sort of library you should dig there too.
What you see may be also consequence of the VBAT brownout erratum. A simple way to check is to add an explicit backup-domain reset *before* any attempt to set up clocks - and as you don't have a backup battery, this should also remain there, too.
JW
2023-07-16 11:15 PM
Hi there,
I'm a collegue of these two guys. So by "lockout mode" @ARed means that STM enters an unknown state at which it doesn't do anything and it can not be reset either internally (watchdog) or externally by NRST pin pulling low.
However, at this state the internal watchdog triggers as if it tries to reset the STM, but the chip doesn't come back. Once in this state, STM will stay "stuck" until the battery is running-on, drains. The only way to reset it is to reset power (e.g. unplug battery).
What you mentioned about VBAT brownout erratum might be relevant here. While monitoring some pins to try and capture something with an oscilloscope, we saw that when this occurred there was a dip in the STM power line, which is the same for VBAT. I add a picture here to show that better.
The scale here is 1V/div.
2023-07-17 02:13 AM
> it doesn't do anything
How do you know that? Can't it be running in its internal bootloader? Or stuck in some unhandled error, or in a loop during attempt to start up? Maybe using a debugger could reveal that.
Nonetheless, if the waveform you've shown is VBAT, regardless of whether it is connected to VDD/VDDA or both, if it goes below ground i.e. to negative as much as you've shown, that may be a concern too, and may have resulted in latchup; so you may want to eliminate that possibility first, by reviewing the power supply arrangement.
JW
2023-07-17 02:14 AM
Trying to set the RCC_BDCR.BDRST bit before clock init makes the LSE hang on LSERDY bit.
while rcc.bdcr.read().lserdy().bit_is_clear() {}
If we try to init the HSE instead of LSE it hangs on HSI16
match clock_source {
ClockSource::LSE => assert!(clocks.lse()),
ClockSource::LSI => assert!(clocks.lsi()),
// Check if HSI16 is enabled
// This operation is sound, as it is an atomic memory access
// that does not modify the memory/read value
ClockSource::HSI16 => {
assert!(unsafe { (&*RCC::ptr()).cr.read().hsion().bit_is_set() })
}
_ => {}
}
2023-07-17 03:49 AM
I don't use and don't understand Rust, and that's probably the case with most users here.
So why don't you just describe the whole sequence you do in terms of registers/bits writes and reads.
> Trying to set the RCC_BDCR.BDRST bit before clock init makes the LSE hang on LSERDY bit.
You should first enable PWR by setting RCC_APB1ENR1.PWREN, in order to be enable backup domain access by setting PWR_CR1.DBP, and then you need to write one to RCC_BDCR.BDRST and then zero to RCC_BDCR.BDRST, before you try to start LSE.
JW