pw_cpu_exception_cortex_m: Add deeper exception information

* Add more informational text about exception flags from the ARM user
guides
* Print the informational text upon exception analysis
* Update documentation example
* Update cortex-m exception analyzer unit test

Change-Id: I1fe27216681ad4eb02b25eaf29d231dad7484e83
Signed-off-by: Shiva Rajagopal <shivarajagopal@google.com>
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/44565
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Armando Montanez <amontanez@google.com>
diff --git a/pw_cpu_exception_cortex_m/docs.rst b/pw_cpu_exception_cortex_m/docs.rst
index 2cb3064..f8718d4 100644
--- a/pw_cpu_exception_cortex_m/docs.rst
+++ b/pw_cpu_exception_cortex_m/docs.rst
@@ -134,8 +134,18 @@
     20210412 15:11:14 INF Exception caused by a usage fault, bus fault.
 
     Active Crash Fault Status Register (CFSR) fields:
-    IBUSERR     Bus fault on instruction fetch.
+    IBUSERR     Instruction bus error.
+        The processor attempted to issue an invalid instruction. It
+        detects the instruction bus error on prefecting, but this
+        flag is only set to 1 if it attempts to issue the faulting
+        instruction. When this bit is set, the processor has not
+        written a fault address to the BFAR.
     UNDEFINSTR  Encountered invalid instruction.
+        The processor has attempted to execute an undefined
+        instruction. When this bit is set to 1, the PC value stacked
+        for the exception return points to the undefined instruction.
+        An undefined instruction is an instruction that the processor
+        cannot decode.
 
     All registers:
     cfsr       0x00010100
diff --git a/pw_cpu_exception_cortex_m/py/exception_analyzer_test.py b/pw_cpu_exception_cortex_m/py/exception_analyzer_test.py
index 8566c5b..78ec218 100644
--- a/pw_cpu_exception_cortex_m/py/exception_analyzer_test.py
+++ b/pw_cpu_exception_cortex_m/py/exception_analyzer_test.py
@@ -210,7 +210,11 @@
             'Exception caused by a bus fault at 0xdeadbeef.',
             '',
             'Active Crash Fault Status Register (CFSR) fields:',
-            'PRECISERR   Precise bus fault.',
+            'PRECISERR   Precise data bus error.',
+            '    A data bus error has occurred, and the PC value stacked for',
+            '    the exception return points to the instruction that caused',
+            '    the fault. When the processor sets this bit to 1, it writes',
+            '    the faulting address to the BFAR',
             'BFARVALID   BFAR is valid.',
             '',
             'All registers:',
diff --git a/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/cortex_m_constants.py b/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/cortex_m_constants.py
index b623c38..b44aa50 100644
--- a/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/cortex_m_constants.py
+++ b/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/cortex_m_constants.py
@@ -72,46 +72,120 @@
 
 # TODO(amontanez): We could probably make a whole module on bit field handling
 # in python.
-BitField = collections.namedtuple('BitField',
-                                  ['name', 'bit_mask', 'description'])
+BitField = collections.namedtuple(
+    'BitField', ['name', 'bit_mask', 'description', 'long_description'])
+
+# Information about faults from:
+# * ARM Cortex-M4 Devices Generic User Guide 4.3.10
+# * ARM Cortex-M33 Devices Generic User Guide 4.2.11
 
 PW_CORTEX_M_CFSR_BIT_FIELDS = [
     BitField('IACCVIOL', PW_CORTEX_M_CFSR_IACCVIOL_MASK,
-             'MPU violation on instruction fetch.'),
+             'Instruction access violation fault.',
+             ('The processor attempted an instruction fetch from a location',
+              'that does not permit execution. The PC value stacked for the',
+              'exception return points to the faulting instruction.')),
     BitField('DACCVIOL', PW_CORTEX_M_CFSR_DACCVIOL_MASK,
-             'MPU violation on memory read/write.'),
+             'Data access violation fault.',
+             ('The processor attempted a load or store at a location that',
+              'does not permit the operation. The PC value stacked for the',
+              'exception return points to the faulting instruction. The',
+              'processor has loaded the MMFAR with the address of the',
+              'attempted access.')),
     BitField('MUNSTKERR', PW_CORTEX_M_CFSR_MUNSTKERR_MASK,
-             'MPU violation on exception return.'),
+             'MemManage fault on unstacking for a return from exception.',
+             ('Unstack for an exception return has caused one or more access',
+              'violations. This fault is chained to the handler. This means',
+              'that when this bit is 1, the original return stack is still',
+              'present. The processor has not adjusted the SP from the',
+              'failing return, and has not performed a new save. The',
+              'processor has not written a fault address to the MMAR.')),
     BitField('MSTKERR', PW_CORTEX_M_CFSR_MSTKERR_MASK,
-             'MPU violation on exception entry.'),
+             'MemManage fault on stacking for exception entry.',
+             ('When this bit is 1, the SP is still adjusted but the values',
+              'in the context area on the stack might be incorrect. The',
+              'processor has not written a fault address to the MMAR.')),
     BitField('MLSPERR', PW_CORTEX_M_CFSR_MLSPERR_MASK,
-             'FPU lazy state preservation failed.'),
+             'MemManage Fault during FPU lazy state preservation.', ''),
     BitField('MMARVALID', PW_CORTEX_M_CFSR_MMARVALID_MASK,
-             'MMFAR register is valid.'),
+             'MMFAR register is valid.', ''),
     BitField('IBUSERR', PW_CORTEX_M_CFSR_IBUSERR_MASK,
-             'Bus fault on instruction fetch.'),
+             'Instruction bus error.',
+             ('The processor attempted to issue an invalid instruction. It',
+              'detects the instruction bus error on prefetching, but this',
+              'flag is only set to 1 if it attempts to issue the faulting',
+              'instruction. When this bit is set, the processor has not',
+              'written a fault address to the BFAR.')),
     BitField('PRECISERR', PW_CORTEX_M_CFSR_PRECISERR_MASK,
-             'Precise bus fault.'),
+             'Precise data bus error.',
+             ('A data bus error has occurred, and the PC value stacked for',
+              'the exception return points to the instruction that caused',
+              'the fault. When the processor sets this bit to 1, it writes',
+              'the faulting address to the BFAR')),
     BitField('IMPRECISERR', PW_CORTEX_M_CFSR_IMPRECISERR_MASK,
-             'Imprecise bus fault.'),
+             'Imprecise data bus error.',
+             ('A data bus error has occurred, but the return address in the',
+              'stack frame is not related to the instruction that caused the',
+              'error. This is an asynchronous fault. Therefore, if it is',
+              'detected when the priority of the current processes is higher',
+              'than the BusFault priority, the BusFault becomes pending and',
+              'becomes active only when the processor returns from all higher',
+              'priority processes. If a precise fault occurs before the',
+              'processor enters the handler for the imprecise BusFault, the',
+              'handler detects both IMPRECISERR set to 1 and one of the',
+              'precise fault status bits set to 1')),
     BitField('UNSTKERR', PW_CORTEX_M_CFSR_UNSTKERR_MASK,
-             'Hardware failure on context restore.'),
+             'BusFault on Unstacking for a return from exception.',
+             ('Unstack for an exception return has caused one or more',
+              'BusFaults. This fault is chained to the handler. This means',
+              'when the processor sets this bit to 1, the original return',
+              'stack is still present. The processor does not adjust the SP',
+              'from the failing return, does not perform a new save, and does',
+              'not write a fault address to the BFAR')),
     BitField('STKERR', PW_CORTEX_M_CFSR_STKERR_MASK,
-             'Hardware failure on context save.'),
+             'BusFault on Stacking for Exception Entry.',
+             ('Stacking for an exception entry has caused one or more',
+              'BusFaults. When the processor sets this bit to 1, the SP is',
+              'still adjusted but the values in the context area on the stack',
+              'might be incorrect. The processor does not write a fault',
+              'address to the BFAR')),
     BitField('LSPERR', PW_CORTEX_M_CFSR_LSPERR_MASK,
-             'FPU lazy state preservation failed.'),
-    BitField('BFARVALID', PW_CORTEX_M_CFSR_BFARVALID_MASK, 'BFAR is valid.'),
+             'BusFault during FPU lazy state preservation.', ''),
+    BitField('BFARVALID', PW_CORTEX_M_CFSR_BFARVALID_MASK, 'BFAR is valid.',
+             ''),
     BitField('UNDEFINSTR', PW_CORTEX_M_CFSR_UNDEFINSTR_MASK,
-             'Encountered invalid instruction.'),
+             'Undefined Instruction UsageFault.',
+             ('The processor has attempted to execute an undefined',
+              'instruction. When this bit is set to 1, the PC value stacked',
+              'for the exception return points to the undefined instruction.',
+              'An undefined instruction is an instruction that the processor',
+              'cannot decode.')),
     BitField('INVSTATE', PW_CORTEX_M_CFSR_INVSTATE_MASK,
-             ('Attempted to execute an instruction with an invalid Execution '
-              'Program Status Register (EPSR) value.')),
+             'Invalid State UsageFault.',
+             ('The processor has attempted to execute an instruction that',
+              'makes illegal use of the EPSR. The PC value stacked for the',
+              'exception return points to the instruction that attempt',
+              'illegal use of the EPSR')),
     BitField('INVPC', PW_CORTEX_M_CFSR_INVPC_MASK,
-             'Program Counter (PC) is not legal.'),
+             'Invalid PC Load UsageFault.',
+             ('The processor has attempted an illegal load of EXC_RETURN',
+              'to the PC, as a result of an invalid context, or an invalid',
+              'EXC_RETURN value. The PC value stacked for the exception',
+              'return points to the instruction that tried to perform the',
+              'illegal load of the PC')),
     BitField('NOCP', PW_CORTEX_M_CFSR_NOCP_MASK,
-             'Coprocessor disabled or not present.'),
-    BitField('STKOF', PW_CORTEX_M_CFSR_STKOF_MASK, 'Stack overflowed.'),
+             'Coprocessor disabled or not present.', ''),
+    BitField('STKOF', PW_CORTEX_M_CFSR_STKOF_MASK, 'Stack overflowed.', ''),
     BitField('UNALIGNED', PW_CORTEX_M_CFSR_UNALIGNED_MASK,
-             'Unaligned load or store. (This exception can be disabled)'),
-    BitField('DIVBYZERO', PW_CORTEX_M_CFSR_DIVBYZERO_MASK, 'Divide by zero.'),
+             'Unaligned access UsageFault.',
+             ('The processor has made an unaligned memory access. This fault',
+              'can be enabled or disabled using the UNALIGN_TRP bit in the',
+              'CCR. Unaligned LDM, STM, LDRD, and STRD instructions always',
+              'fault irrespective of the CCR setting.')),
+    BitField('DIVBYZERO', PW_CORTEX_M_CFSR_DIVBYZERO_MASK, 'Divide by zero.',
+             ('The processor has executed an SDIV or UDIV instruction with',
+              'a divisor of 0. The PC value stacked for the exception',
+              'return points to the instruction that performed the divide',
+              'by zero. This fault can be enabled or disabled using the',
+              'DIV_0_TRP bit in the CCR.')),
 ]
diff --git a/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/exception_analyzer.py b/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/exception_analyzer.py
index c246a80..2810b64 100644
--- a/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/exception_analyzer.py
+++ b/pw_cpu_exception_cortex_m/py/pw_cpu_exception_cortex_m/exception_analyzer.py
@@ -126,6 +126,10 @@
         fields = []
         for field in self.active_cfsr_fields():
             fields.append(f'{field.name:<11} {field.description}')
+            if isinstance(field.long_description, tuple):
+                long_desc = '    {}'.format('\n    '.join(
+                    field.long_description))
+                fields.append(long_desc)
         return '\n'.join(fields)
 
     def __str__(self):