0
\$\begingroup\$

I always assumed that when an unhandled ARM exception occurs (i.e segfault), the microcontroller just stops executing code. This assumption comes from debugging observation: most MCU I worked with stopped in an assembly routine when segfaulting.

As I'm building more safety-critical applications, I want to better understand what is the effect and impact of those errors.

  • Can I assume the peripheral will stay configured as is?
  • Can I assume the GPIOs will stay in their current state?
  • Is the unhandled exception behaviour defined?

Tips and resources to learn more about the fundamentals are appreciated.
Thanks for your time.

\$\endgroup\$

3 Answers 3

3
\$\begingroup\$

You should handle all exceptions/fault interrupts, so there should not be any "unhandled" ones. Unhandled means that the exception and/or interrupt table has an instance which sends the program counter nowhere/to the wrong address.

Please note that in circuit debuggers might be designed to halt upon encountering such problems, to give you a chance to spot one. While the real program will hopefully just reset the MCU and start over - at watchdog reset if not sooner.

Can I assume the peripheral will stay configured as is?
Can I assume the GPIOs will stay in their current state?

No. Hardware peripheral registers are to be regarded as RAM, which in turn is traditionally regarded as less reliable than NVM. In systems where very long up-times are expected, it is custom to initialize those registers on regular basis, by copying down the default settings from flash.

Additionally, in safety-critical applications, I usually recommend to implement a vector table where every unhandled interrupt that you shouldn't be getting results in a defensive ISR. This ISR does nothing but shutting off the source of its interrupt and then returns. (This of course depending on the nature of the application: is the safe state a stop or is it "keep running as well as you can".) That is, if you aren't using UART in your application, still write a UART ISR which does nothing but shutting off the UART interrupt, that you shouldn't be getting.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for your answer. Especially for the safety-critical applications tips. Do you have any recommendations on resources (appnotes; books) about safety-critical firmware design? Or maybe an open-source project that does it well? I really want to improve myself and my project in those areas. \$\endgroup\$
    – gbt
    Commented Jul 6, 2020 at 15:16
  • \$\begingroup\$ @gberth You won't find much in the way of open source, since safety-critical is almost always expensive and proprietary.The #1 must-read resource is MISRA-C. On the topic of C programming, there's also two somewhat dated but important books: Safer C / Les Hatton and C Traps and Pitfalls / Andrew Koenig. A lot from the original MISRA-C was based on these books. Also, I wrote a summary of common techniques used here. \$\endgroup\$
    – Lundin
    Commented Jul 7, 2020 at 6:40
  • \$\begingroup\$ Thanks for your answer. Your summary of common techniques are definitively worth reading. I will take a look at Safer-C as well . \$\endgroup\$
    – gbt
    Commented Jul 8, 2020 at 21:04
3
\$\begingroup\$

It is all up to the software developer. Has nothing to do with the hardware. The hardware has known locations for where handler code should go (be it an exception table or vector table depending on which ARM core you are talking about, or in general for any core).

The software developer chooses how to handle each. Worst case is that no consideration is made for handlers and where the entry points are may instead be code, so in that case the software may take a partial reboot path in that it starts executing near the beginning but not at the beginning, but the chip was not in reset so how that code is written and where you enter determines if you crash or not and how.

If nothing else the developer should have a handler even if it is an infinite loop which to your question would mean nothing changes, the peripherals stay in their as-was state.

And then there is some sense of full support, but that is part of an overall system design, what, from a system perspective do you want the product to do for those exceptions, or within an exception if more info can be determined what to do for each sub category of exception. Is this an rtos do you attempt to abandon an application and keep the system going, etc. Takes a lot of design to determine who caused the fault in an example like that and what keep going means.

The long and short of it is the answer is 100% in the developers hands, the hardware has little to do with it. You do the system design and from that the hardware and software design, and within that the handlers.

ARM is pretty good with their documentation (should have read that first before asking here) so the various exceptions are defined.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your answer @old_timer. Reading the ARM documentation is a great idea. Do you have any further documentation (appnotes; books) about firmware fault handling design? Or maybe an open-source project that does it well? I'd like to better understand the design considerations. \$\endgroup\$
    – gbt
    Commented Jul 6, 2020 at 15:10
  • \$\begingroup\$ Not possible for me to know what your requirements are therefore its up to you. The chip documentation covers the chips capabilities, the arm documentation covers the arm core, and you cover the system design. These documents will cover what features are available to you, what faults might happen, and then you can determine if that is a real risk and what to do with each. \$\endgroup\$
    – old_timer
    Commented Jul 6, 2020 at 16:38
1
\$\begingroup\$

Most likely the startup code you are using will have default handlers to exceptions you don't explicitly say you want to handle yourself in your code. These handlers do get executed, and usually by default they just go into while(1); loop or equivalent. When debugging, the debugger and the UI do know that now a fault happened, so most likely they stop execution and break at the fault entry point, because that is most convenient for the person debugging, and the code can't continue anyway.

Some things are not enabled by default by the CPU, like division by zero won't raise an Usage Fault handler to execute, but it all depends on your compiler startup code if it provides that.

Same thing happens if for example you enable UART receiver interrupt, but forget to install the handler, so again the default code catches it, even though it is a perfectly valid interrupt.

So yes, always install specific or generic handler to all vectors, even if these are not used. And make sure the code makes known something unexpected happens, because it is a bug anyway. During developement, blink a LED or something so it can be debugged why it happened. In release code, restart the MCU, if it can be done safely.

In general, why a fault happens, depends of course what caused it, and how much you can trust a system state after it.

If you divide by zero, and have the fault generation enabled for that, it won't of course destroy any GPIO or peripheral state.

If there is a fault from reading or writing to a memory address that is non-existent (could include the NULL address), you can't really know what has gone wrong, if a pointer has already become invalid, and you can't really continue safely from that any more.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.