blob: c72c8bd80ef8ffa03ce2d3c70fc6ed429c33507f [file]
.. _hardfaults:
==========
Hardfaults
==========
ARMv7
=====
Cortex-M3 and Cortex-M4
-----------------------
The most popular CPUs in current MCU designs are the Cortex-M3 (ARMv7-M) and
the Cortex-M4 (ARMv7E-M). Handling of these two architectures is almost
identical in NuttX (unless hardware floating point is enabled).
SVCALL
------
NuttX uses the SVCALL software interrupt in order to perform certain steps
in the context switching for the Cortex-M3 and Cortext-M4.
This sequence of logic appears in several places:
* Create a short critical section by disabling exceptions,
* Perform some set-up,
* Initiate the software exception / SVCALL, and
* When the software exception processing returns, re-enable exceptions.
.. note:: There is a technical difference between interrupts and exceptions.
In this section the term exception will be used. It is probably
more accurate since interrupts are really exceptions that result
from device interrupt lines. Furthermore, when we refer to
exceptions in this section, we are referring specifically to
ARMv7-M configurable exceptions.
Disabling Interrupts via the PRIMASK register
---------------------------------------------
The ARMv7-M architecture supports a register called the ``PRIMASK`` register.
The ``PRIMASK`` register contains a single valid bit. If that bit is set to
one, then exceptions are disabled. If that bit is zero, exceptions are enabled.
More correctly, when this bit is set to one, it prevents the activation of all
exceptions with configurable priority.
The original NuttX implementation used this ``PRIMASK`` register to enable
and disable exceptions. Things now get interesting in the sequence of logic
listed above because the ``PRIMASK`` bit also disables the SVCALL exception!
So, instead of taking the ``SVCALL`` exception vector, the Cortex-M3/4
generates a hardfault exception (see ARM.com's discussion of
`Activation Levels <https://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337g/Chdbdfjf.html>`_).
These hardfaults are not really a problem. The design of the NuttX hardfault
handler expects these exceptions and does the right thing.
However, the occurrence of hardfaults may come as a surprise to many people
and especially to some debuggers.
Hardfaults and Debuggers
------------------------
These hardfaults only become a technical issue when dealing with a debugger.
What does the debugger do when the hardfault occurs?
We need to back off and think about some system philosophy here.
The deep philosophical question here is: Who is in charge of system integrity?
The debugger or the RTOS?
If you are running a primitive NoOS program (like the famous blinky test
program), then you are running a barebones system and you need
all of the help you can get. So having the debugger make decisions about what
is the proper behavior of the blinky program and what is not is a good
thing for you.
But if you are using an advanced RTOS, then the RTOS will want to take
responsibility of the health of your system and now there is the possibility
of inconsistencies between the decisions that the RTOS makes and the decisions
that your debugger makes this hardfault handling is a perfect example here.
In NuttX, the hardfaults are controlled by the RTOS, but some debuggers will
break when the hardfault occurs and make debugging impossible.
Some people might take issue with this. Breaking on hardfault can make
debugging easier, since the break happens in the throwing context and you have
a hope of obtaining a backtrace and debugging the throwing side callstack.
However, I would suggest that putting a break point on ``up_assert()`` would
accomplish the same thing without being so intrusive.
How did the debugger know that the hardfault occurred? It knew because of
settings in the "ARM's Debug Exception and Monitor Control Register" or
``DEMCR``. Proper settings of the ``DEMCR`` register will allow debugger
to get break exceptions when a hardfault occrs.
So one workaround is to just reconfigure the ``DEMCR`` register so that break
exceptions are no longer generated when hardfaults occur.
Here is an example of such logic for the LPC43xx MCU.
Decoupling the hardfault from the break exception in this way does not work
with all debuggers, however. Presumably because some debuggers re-enable break
exceptions on hardfaults.
Disabling Interrupts via the BASEPRI register
---------------------------------------------
The ARMv7-M architecture supports another way to disable exceptions using
a register called the ``BASEPRI`` register. ARMv7-M exceptions are prioritized.
Each exception can be assigned an 8-bit priority. If the ``BASEPRI`` register
is set to a non-zero priority value, then it will filter exceptions in this sense:
* Exceptions with priority lower than or equal to the ``BASEPRI`` register
will be disabled.
* Exceptions with priority higher than the ``BASEPRI`` register will still
be enabled.
Normally this interrupt prioritization is used to support nested interrupt
handling, but it can also be used for disabling of all exceptions
if configured properly.
.. note:: In the ARMv7-M, higher values correspond to lower priority.
This can be really confusing!
NuttX supports a configuration option called ``CONFIG_ARMV7M_USEBASEPRI``.
If this option is selected, then the exception prioritization and control
logic will be configured to use the ``BASEPRI`` register instead of the
``PRIMASK`` register to disable exceptions.
This configuration includes the following changes in the behavior:
* Normal interrupts and exceptions are restricted to the range
``{ lowest priority ... (highest priority - 1) }``.
* The priority of the ``SVCALL`` exception is set to highest priority.
* When exceptions are enabled, the ``BASEPRI`` register is set to zero,
enabling exceptions of all priorities.
* When exceptions are disabled, the ``BASEPRI`` register is set to
``(highest priority - 1)``, disabling all exceptions except for the
``SVCALL`` exception.
In this way, the ``SVCALL`` exception remains enabled when exceptions
are disabled and no hardfault occurs.
.. note:: The above is inaccurate on several counts. It was simplified
to make the discussion sane. Not only do higher values correspond
to lower priorities, but the increment between consecutive 8-bit
priority values is probably not one. The supported maximum and
minimum priority values (as well as the step in each priority value)
may be different for each MCU. To handle this, these values are
exported in NuttX for each ARMv7-M architecture by header files at
``nuttx/arch/arm/include/<chip>/chip.h``.