pw_cpu_exception_cortex_m: Expose LogExceptionAnalysis()
Exposes an internal part of the support backend to log the
exception analysis as an optional utility API.
The cpu_state.cc file is renamed to support.cc to reflect what
the facade/backend that the file is associated with.
The cpu_state.h is pulled out to a separate target to remove
unnecessary circular dependencies within the module.
In addition the last remaining TODOs for pwbug/296 are removed and
the Bazel build files are cleaned up a little bit.
Also updates the docs to include the configuration options.
Bug: 296
Change-Id: Id2ea6adc5dc4f3f99c2d9cf15ea048886739736a
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/78860
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Ewout van Bekkum <ewout@google.com>
diff --git a/pw_cpu_exception_cortex_m/BUILD.bazel b/pw_cpu_exception_cortex_m/BUILD.bazel
index 73da801..9eea2aa 100644
--- a/pw_cpu_exception_cortex_m/BUILD.bazel
+++ b/pw_cpu_exception_cortex_m/BUILD.bazel
@@ -28,16 +28,37 @@
)
pw_cc_library(
- name = "support_armv7m",
- srcs = ["cpu_state.cc"],
- hdrs = [
- "public/pw_cpu_exception_cortex_m/cpu_state.h",
- "public_overrides/pw_cpu_exception_backend/state.h",
+ name = "cpu_state",
+ hdrs = ["public/pw_cpu_exception_cortex_m/cpu_state.h"],
+ includes = ["public"],
+ deps = [
+ "//pw_preprocessor",
+ "//pw_preprocessor:arch",
],
+)
+
+pw_cc_library(
+ name = "util",
+ srcs = ["util.cc"],
+ hdrs = ["public/pw_cpu_exception_cortex_m/util.h"],
includes = ["public"],
deps = [
":config",
":cortex_m_constants",
+ ":cpu_state",
+ "//pw_log",
+ "//pw_preprocessor:arch",
+ ],
+)
+
+pw_cc_library(
+ name = "support",
+ srcs = ["support.cc"],
+ deps = [
+ ":config",
+ ":cortex_m_constants",
+ ":cpu_state",
+ ":util",
"//pw_log",
"//pw_preprocessor",
"//pw_preprocessor:arch",
@@ -46,13 +67,15 @@
)
pw_cc_library(
- name = "proto_dump_armv7m",
+ name = "proto_dump",
srcs = ["proto_dump.cc"],
hdrs = ["public/pw_cpu_exception_cortex_m/proto_dump.h"],
+ includes = ["public"],
deps = [
":config",
+ ":cpu_state",
":cpu_state_protos",
- ":support_armv7m",
+ ":support",
"//pw_protobuf",
"//pw_status",
"//pw_stream",
@@ -64,17 +87,20 @@
srcs = ["pw_cpu_exception_cortex_m_protos/cpu_state.proto"],
)
-# TODO(pwbug/296): The *_armv7m libraries work on ARMv8-M, but needs some minor
-# patches for complete correctness. Add *_armv8m targets that use the same files
-# but provide preprocessor defines to enable/disable architecture specific code.
pw_cc_library(
- name = "cpu_exception_armv7m",
+ name = "cpu_exception",
srcs = ["entry.cc"],
+ hdrs = [
+ "public/pw_cpu_exception_cortex_m/cpu_state.h",
+ "public_overrides/pw_cpu_exception_backend/state.h",
+ ],
+ includes = ["public"],
deps = [
":config",
+ ":cpu_state",
":cortex_m_constants",
- ":proto_dump_armv7m",
- ":support_armv7m",
+ ":proto_dump",
+ ":support",
# TODO(pwbug/101): Need to add support for facades/backends to Bazel.
"//pw_cpu_exception",
"//pw_preprocessor",
@@ -89,11 +115,10 @@
deps = [
":config",
":cortex_m_constants",
+ ":cpu_state",
":cpu_state_protos",
- ":proto_dump_armv7m",
- ":support_armv7m",
- # TODO(pwbug/101): Need to add support for facades/backends to Bazel.
- "//pw_cpu_exception",
+ ":proto_dump",
+ ":support",
"//pw_log",
"//pw_protobuf",
"//pw_status",
@@ -115,6 +140,7 @@
"exception_entry_test.cc",
],
deps = [
- ":cpu_exception_armv7m",
+ ":cpu_exception",
+ ":cpu_state",
],
)
diff --git a/pw_cpu_exception_cortex_m/BUILD.gn b/pw_cpu_exception_cortex_m/BUILD.gn
index 7ae1640..53d05d5 100644
--- a/pw_cpu_exception_cortex_m/BUILD.gn
+++ b/pw_cpu_exception_cortex_m/BUILD.gn
@@ -51,13 +51,13 @@
deps = [
":config",
":cortex_m_constants",
- ":cpu_exception",
+ ":util",
"$dir_pw_cpu_exception:support.facade",
"$dir_pw_preprocessor:arch",
dir_pw_log,
dir_pw_string,
]
- sources = [ "cpu_state.cc" ]
+ sources = [ "support.cc" ]
}
# The following targets are deprecated, use ":support" instead.
@@ -68,6 +68,19 @@
public_deps = [ ":support" ]
}
+pw_source_set("util") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_cpu_exception_cortex_m/util.h" ]
+ public_deps = [ ":cpu_state" ]
+ deps = [
+ ":config",
+ ":cortex_m_constants",
+ "$dir_pw_preprocessor:arch",
+ dir_pw_log,
+ ]
+ sources = [ "util.cc" ]
+}
+
pw_source_set("proto_dump") {
public_configs = [ ":public_include_path" ]
public_deps = [
@@ -96,19 +109,23 @@
sources = [ "pw_cpu_exception_cortex_m_protos/cpu_state.proto" ]
}
-# TODO(pwbug/296): The *_armv7m libraries work on ARMv8-M, but needs some minor
-# patches for complete correctness. Add *_armv8m targets that use the same files
-# but provide preprocessor defines to enable/disable architecture specific code.
+pw_source_set("cpu_state") {
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_cpu_exception_cortex_m/cpu_state.h" ]
+ public_deps = [
+ "$dir_pw_preprocessor",
+ "$dir_pw_preprocessor:arch",
+ ]
+}
+
pw_source_set("cpu_exception") {
public_configs = [
":backend_config",
":public_include_path",
]
- public = [
- "public/pw_cpu_exception_cortex_m/cpu_state.h",
- "public_overrides/pw_cpu_exception_backend/state.h",
- ]
+ public = [ "public_overrides/pw_cpu_exception_backend/state.h" ]
public_deps = [
+ ":cpu_state",
"$dir_pw_preprocessor",
"$dir_pw_preprocessor:arch",
]
@@ -122,14 +139,12 @@
public_deps = [ ":cpu_exception" ]
}
-# TODO(pwbug/296): The *_armv7m libraries work on ARMv8-M, but needs some minor
-# patches for complete correctness. Add *_armv8m targets that use the same files
-# but provide preprocessor defines to enable/disable architecture specific code.
pw_source_set("cpu_exception.impl") {
sources = [ "entry.cc" ]
deps = [
":config",
":cortex_m_constants",
+ ":cpu_state",
"$dir_pw_cpu_exception:entry.facade",
"$dir_pw_cpu_exception:handler",
"$dir_pw_preprocessor:arch",
@@ -147,7 +162,7 @@
pw_source_set("snapshot") {
public_configs = [ ":public_include_path" ]
public_deps = [
- ":cpu_exception",
+ ":cpu_state",
":cpu_state_protos.pwpb",
"$dir_pw_thread:protos.pwpb",
"$dir_pw_thread:snapshot",
@@ -200,6 +215,7 @@
deps = [
":cortex_m_constants",
":cpu_exception",
+ ":cpu_state",
"$dir_pw_cpu_exception:entry",
"$dir_pw_cpu_exception:handler",
"$dir_pw_cpu_exception:support",
diff --git a/pw_cpu_exception_cortex_m/CMakeLists.txt b/pw_cpu_exception_cortex_m/CMakeLists.txt
index 4c41786..36603bd 100644
--- a/pw_cpu_exception_cortex_m/CMakeLists.txt
+++ b/pw_cpu_exception_cortex_m/CMakeLists.txt
@@ -24,12 +24,21 @@
pw_cpu_exception_cortex_m_private/config.h
)
+pw_add_module_library(pw_cpu_exception_cortex_m.cpu_state
+ PUBLIC_DEPS
+ pw_preprocessor
+ pw_preprocessor.arch
+ HEADERS
+ public/pw_cpu_exception_cortex_m/cpu_state.h
+)
+
pw_add_module_library(pw_cpu_exception_cortex_m.cpu_exception
IMPLEMENTS_FACADES
pw_cpu_exception.entry
PUBLIC_DEPS
pw_preprocessor
pw_preprocessor.arch
+ pw_cpu_exception_cortex_m.cpu_state
PRIVATE_DEPS
pw_cpu_exception.handler
pw_cpu_exception_cortex_m.config
@@ -37,21 +46,35 @@
SOURCES
entry.cc
HEADERS
- public/pw_cpu_exception_cortex_m/cpu_state.h
public_overrides/pw_cpu_exception_backend/state.h
)
+pw_add_module_library(pw_cpu_exception_cortex_m.util
+ PUBLIC_DEPS
+ pw_cpu_exception_cortex_m.cpu_state
+ PRIVATE_DEPS
+ pw_cpu_exception_cortex_m.config
+ pw_cpu_exception_cortex_m.constants
+ pw_log
+ pw_preprocessor.arch
+ SOURCES
+ util.cc
+ HEADERS
+ public/pw_cpu_exception_cortex_m/util.h
+)
+
pw_add_module_library(pw_cpu_exception_cortex_m.support
IMPLEMENTS_FACADES
pw_cpu_exception.support
PRIVATE_DEPS
pw_cpu_exception_cortex_m.config
pw_cpu_exception_cortex_m.constants
+ pw_cpu_exception_cortex_m.util
pw_log
pw_preprocessor.arch
pw_string
SOURCES
- cpu_state.cc
+ support.cc
)
pw_proto_library(pw_cpu_exception_cortex_m.cpu_state_protos
@@ -61,7 +84,7 @@
pw_add_module_library(pw_cpu_exception_cortex_m.proto_dump
PUBLIC_DEPS
- pw_cpu_exception.entry
+ pw_cpu_exception_cortex_m.cpu_state
pw_protobuf
pw_status
pw_stream
@@ -76,8 +99,8 @@
pw_add_module_library(pw_cpu_exception_cortex_m.snapshot
PUBLIC_DEPS
+ pw_cpu_exception_cortex_m.cpu_state
pw_cpu_exception_cortex_m.cpu_state_protos.pwpb
- pw_cpu_exception_cortex_m.cpu_exception
pw_protobuf
pw_status
PRIVATE_DEPS
diff --git a/pw_cpu_exception_cortex_m/cpu_state.cc b/pw_cpu_exception_cortex_m/cpu_state.cc
deleted file mode 100644
index b41c168..0000000
--- a/pw_cpu_exception_cortex_m/cpu_state.cc
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright 2019 The Pigweed Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#include "pw_cpu_exception_cortex_m/cpu_state.h"
-
-#include <cinttypes>
-#include <cstdint>
-#include <span>
-
-#include "pw_cpu_exception/support.h"
-#include "pw_cpu_exception_cortex_m_private/config.h"
-#include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
-#include "pw_log/log.h"
-#include "pw_preprocessor/arch.h"
-#include "pw_string/string_builder.h"
-
-namespace pw::cpu_exception {
-namespace cortex_m {
-namespace {
-
-[[maybe_unused]] void AnalyzeCfsr(const uint32_t cfsr) {
- if (cfsr == 0) {
- return;
- }
-
- PW_LOG_INFO("Active CFSR fields:");
-
- // Memory managment fault fields.
- if (cfsr & kCfsrIaccviolMask) {
- PW_LOG_ERROR(" IACCVIOL: MPU violation on instruction fetch");
- }
- if (cfsr & kCfsrDaccviolMask) {
- PW_LOG_ERROR(" DACCVIOL: MPU violation on memory read/write");
- }
- if (cfsr & kCfsrMunstkerrMask) {
- PW_LOG_ERROR(" MUNSTKERR: 'MPU violation on exception return");
- }
- if (cfsr & kCfsrMstkerrMask) {
- PW_LOG_ERROR(" MSTKERR: MPU violation on exception entry");
- }
- if (cfsr & kCfsrMlsperrMask) {
- PW_LOG_ERROR(" MLSPERR: MPU violation on lazy FPU state preservation");
- }
- if (cfsr & kCfsrMmarvalidMask) {
- PW_LOG_ERROR(" MMARVALID: MMFAR register is valid");
- }
-
- // Bus fault fields.
- if (cfsr & kCfsrIbuserrMask) {
- PW_LOG_ERROR(" IBUSERR: Bus fault on instruction fetch");
- }
- if (cfsr & kCfsrPreciserrMask) {
- PW_LOG_ERROR(" PRECISERR: Precise bus fault");
- }
- if (cfsr & kCfsrImpreciserrMask) {
- PW_LOG_ERROR(" IMPRECISERR: Imprecise bus fault");
- }
- if (cfsr & kCfsrUnstkerrMask) {
- PW_LOG_ERROR(" UNSTKERR: Derived bus fault on exception context save");
- }
- if (cfsr & kCfsrStkerrMask) {
- PW_LOG_ERROR(" STKERR: Derived bus fault on exception context restore");
- }
- if (cfsr & kCfsrLsperrMask) {
- PW_LOG_ERROR(" LSPERR: Derived bus fault on lazy FPU state preservation");
- }
- if (cfsr & kCfsrBfarvalidMask) {
- PW_LOG_ERROR(" BFARVALID: BFAR register is valid");
- }
-
- // Usage fault fields.
- if (cfsr & kCfsrUndefinstrMask) {
- PW_LOG_ERROR(" UNDEFINSTR: Encountered invalid instruction");
- }
- if (cfsr & kCfsrInvstateMask) {
- PW_LOG_ERROR(
- " INVSTATE: Attempted to execute an instruction with an invalid "
- "Execution Program Status Register (EPSR) value");
- }
- if (cfsr & kCfsrInvpcMask) {
- PW_LOG_ERROR(" INVPC: Program Counter (PC) is not legal");
- }
- if (cfsr & kCfsrNocpMask) {
- PW_LOG_ERROR(" NOCP: Coprocessor disabled or not present");
- }
- if (cfsr & kCfsrUnalignedMask) {
- PW_LOG_ERROR(" UNALIGNED: Unaligned memory access");
- }
- if (cfsr & kCfsrDivbyzeroMask) {
- PW_LOG_ERROR(" DIVBYZERO: Division by zero");
- }
-#if _PW_ARCH_ARM_V8M_MAINLINE
- if (cfsr & kCfsrStkofMask) {
- PW_LOG_ERROR(" STKOF: Stack overflowed");
- }
-#endif // _PW_ARCH_ARM_V8M_MAINLINE
-}
-
-void AnalyzeException(const pw_cpu_exception_State& cpu_state) {
- // This provides a high-level assessment of the cause of the exception.
- // These conditionals are ordered by priority to ensure the most critical
- // issues are highlighted first. These are not mutually exclusive; a bus fault
- // could occur during the handling of a MPU violation, causing a nested fault.
- if (cpu_state.extended.hfsr & kHfsrForcedMask) {
- PW_LOG_CRITICAL("Encountered a nested CPU fault (See active CFSR fields)");
- }
-#if _PW_ARCH_ARM_V8M_MAINLINE
- if (cpu_state.extended.cfsr & kCfsrStkofMask) {
- if (cpu_state.extended.exc_return & kExcReturnStackMask) {
- PW_LOG_CRITICAL("Encountered stack overflow in thread mode");
- } else {
- PW_LOG_CRITICAL("Encountered main (interrupt handler) stack overflow");
- }
- }
-#endif // _PW_ARCH_ARM_V8M_MAINLINE
- if (cpu_state.extended.cfsr & kCfsrMemFaultMask) {
- if (cpu_state.extended.cfsr & kCfsrMmarvalidMask) {
- PW_LOG_CRITICAL(
- "Encountered Memory Protection Unit (MPU) violation at 0x%08" PRIx32,
- cpu_state.extended.mmfar);
- } else {
- PW_LOG_CRITICAL("Encountered Memory Protection Unit (MPU) violation");
- }
- }
- if (cpu_state.extended.cfsr & kCfsrBusFaultMask) {
- if (cpu_state.extended.cfsr & kCfsrBfarvalidMask) {
- PW_LOG_CRITICAL("Encountered bus fault at 0x%08" PRIx32,
- cpu_state.extended.bfar);
- } else {
- PW_LOG_CRITICAL("Encountered bus fault");
- }
- }
- if (cpu_state.extended.cfsr & kCfsrUsageFaultMask) {
- PW_LOG_CRITICAL("Encountered usage fault (See active CFSR fields)");
- }
- if ((cpu_state.extended.icsr & kIcsrVectactiveMask) == kNmiIsrNum) {
- PW_LOG_INFO("Encountered non-maskable interrupt (NMI)");
- }
-#if PW_CPU_EXCEPTION_CORTEX_M_EXTENDED_CFSR_DUMP
- AnalyzeCfsr(cpu_state.extended.cfsr);
-#endif // PW_CPU_EXCEPTION_CORTEX_M_EXTENDED_CFSR_DUMP
-}
-
-} // namespace
-} // namespace cortex_m
-
-std::span<const uint8_t> RawFaultingCpuState(
- const pw_cpu_exception_State& cpu_state) {
- return std::span(reinterpret_cast<const uint8_t*>(&cpu_state),
- sizeof(cpu_state));
-}
-
-// Using this function adds approximately 100 bytes to binary size.
-void ToString(const pw_cpu_exception_State& cpu_state,
- const std::span<char>& dest) {
- StringBuilder builder(dest);
- const cortex_m::ExceptionRegisters& base = cpu_state.base;
- const cortex_m::ExtraRegisters& extended = cpu_state.extended;
-
-#define _PW_FORMAT_REGISTER(state_section, name) \
- builder.Format("%s=0x%08" PRIx32 "\n", #name, state_section.name)
-
- // Other registers.
- if (base.pc != cortex_m::kUndefinedPcLrOrPsrRegValue) {
- _PW_FORMAT_REGISTER(base, pc);
- }
- if (base.lr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
- _PW_FORMAT_REGISTER(base, lr);
- }
- if (base.psr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
- _PW_FORMAT_REGISTER(base, psr);
- }
- _PW_FORMAT_REGISTER(extended, msp);
- _PW_FORMAT_REGISTER(extended, psp);
- _PW_FORMAT_REGISTER(extended, exc_return);
-#if _PW_ARCH_ARM_V8M_MAINLINE
- _PW_FORMAT_REGISTER(extended, msplim);
- _PW_FORMAT_REGISTER(extended, psplim);
-#endif // _PW_ARCH_ARM_V8M_MAINLINE
- _PW_FORMAT_REGISTER(extended, cfsr);
- _PW_FORMAT_REGISTER(extended, mmfar);
- _PW_FORMAT_REGISTER(extended, bfar);
- _PW_FORMAT_REGISTER(extended, icsr);
- _PW_FORMAT_REGISTER(extended, hfsr);
- _PW_FORMAT_REGISTER(extended, shcsr);
- _PW_FORMAT_REGISTER(extended, control);
-
- // General purpose registers.
- _PW_FORMAT_REGISTER(base, r0);
- _PW_FORMAT_REGISTER(base, r1);
- _PW_FORMAT_REGISTER(base, r2);
- _PW_FORMAT_REGISTER(base, r3);
- _PW_FORMAT_REGISTER(extended, r4);
- _PW_FORMAT_REGISTER(extended, r5);
- _PW_FORMAT_REGISTER(extended, r6);
- _PW_FORMAT_REGISTER(extended, r7);
- _PW_FORMAT_REGISTER(extended, r8);
- _PW_FORMAT_REGISTER(extended, r9);
- _PW_FORMAT_REGISTER(extended, r10);
- _PW_FORMAT_REGISTER(extended, r11);
- _PW_FORMAT_REGISTER(base, r12);
-
-#undef _PW_FORMAT_REGISTER
-}
-
-// Using this function adds approximately 100 bytes to binary size.
-void LogCpuState(const pw_cpu_exception_State& cpu_state) {
- const cortex_m::ExceptionRegisters& base = cpu_state.base;
- const cortex_m::ExtraRegisters& extended = cpu_state.extended;
-
- cortex_m::AnalyzeException(cpu_state);
-
- PW_LOG_INFO("All captured CPU registers:");
-
-#define _PW_LOG_REGISTER(state_section, name) \
- PW_LOG_INFO(" %-10s 0x%08" PRIx32, #name, state_section.name)
-
- // Other registers.
- if (base.pc != cortex_m::kUndefinedPcLrOrPsrRegValue) {
- _PW_LOG_REGISTER(base, pc);
- }
- if (base.lr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
- _PW_LOG_REGISTER(base, lr);
- }
- if (base.psr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
- _PW_LOG_REGISTER(base, psr);
- }
- _PW_LOG_REGISTER(extended, msp);
- _PW_LOG_REGISTER(extended, psp);
- _PW_LOG_REGISTER(extended, exc_return);
-#if _PW_ARCH_ARM_V8M_MAINLINE
- _PW_LOG_REGISTER(extended, msplim);
- _PW_LOG_REGISTER(extended, psplim);
-#endif // _PW_ARCH_ARM_V8M_MAINLINE
- _PW_LOG_REGISTER(extended, cfsr);
- _PW_LOG_REGISTER(extended, mmfar);
- _PW_LOG_REGISTER(extended, bfar);
- _PW_LOG_REGISTER(extended, icsr);
- _PW_LOG_REGISTER(extended, hfsr);
- _PW_LOG_REGISTER(extended, shcsr);
- _PW_LOG_REGISTER(extended, control);
-
- // General purpose registers.
- _PW_LOG_REGISTER(base, r0);
- _PW_LOG_REGISTER(base, r1);
- _PW_LOG_REGISTER(base, r2);
- _PW_LOG_REGISTER(base, r3);
- _PW_LOG_REGISTER(extended, r4);
- _PW_LOG_REGISTER(extended, r5);
- _PW_LOG_REGISTER(extended, r6);
- _PW_LOG_REGISTER(extended, r7);
- _PW_LOG_REGISTER(extended, r8);
- _PW_LOG_REGISTER(extended, r9);
- _PW_LOG_REGISTER(extended, r10);
- _PW_LOG_REGISTER(extended, r11);
- _PW_LOG_REGISTER(base, r12);
-
-#undef _PW_LOG_REGISTER
-}
-
-} // namespace pw::cpu_exception
diff --git a/pw_cpu_exception_cortex_m/docs.rst b/pw_cpu_exception_cortex_m/docs.rst
index e441fc5..b8726b2 100644
--- a/pw_cpu_exception_cortex_m/docs.rst
+++ b/pw_cpu_exception_cortex_m/docs.rst
@@ -236,3 +236,27 @@
r10 0xbd15c968
r11 0x759b95ab
r12 0x00000000
+
+Module Configuration Options
+============================
+The following configurations can be adjusted via compile-time configuration of
+this module, see the
+:ref:`module documentation <module-structure-compile-time-configuration>` for
+more details.
+
+.. c:macro:: PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL
+
+ The log level to use for this module. Logs below this level are omitted.
+
+ This defaults to ``PW_LOG_LEVEL_DEBUG``.
+
+.. c:macro:: PW_CPU_EXCEPTION_CORTEX_M_EXTENDED_CFSR_DUMP
+
+ Enables extended logging in pw::cpu_exception::LogCpuState() and
+ pw::cpu_exception::cortex_m::LogExceptionAnalysis() that dumps the active
+ CFSR fields with help strings. This is disabled by default since it
+ increases the binary size by >1.5KB when using plain-text logs, or ~460
+ Bytes when using tokenized logging. It's useful to enable this for device
+ bringup until your application has an end-to-end crash reporting solution.
+
+ This is disabled by default.
diff --git a/pw_cpu_exception_cortex_m/public/pw_cpu_exception_cortex_m/util.h b/pw_cpu_exception_cortex_m/public/pw_cpu_exception_cortex_m/util.h
new file mode 100644
index 0000000..a760bb5
--- /dev/null
+++ b/pw_cpu_exception_cortex_m/public/pw_cpu_exception_cortex_m/util.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include "pw_cpu_exception_cortex_m/cpu_state.h"
+
+namespace pw::cpu_exception::cortex_m {
+
+void LogExceptionAnalysis(const pw_cpu_exception_State& cpu_state);
+
+} // namespace pw::cpu_exception::cortex_m
diff --git a/pw_cpu_exception_cortex_m/pw_cpu_exception_cortex_m_private/config.h b/pw_cpu_exception_cortex_m/pw_cpu_exception_cortex_m_private/config.h
index c34a49b..eb0b7dd 100644
--- a/pw_cpu_exception_cortex_m/pw_cpu_exception_cortex_m_private/config.h
+++ b/pw_cpu_exception_cortex_m/pw_cpu_exception_cortex_m_private/config.h
@@ -31,8 +31,9 @@
#define PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL PW_LOG_LEVEL_DEBUG
#endif // PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL
-// Enables extended logging in pw::cpu_exception::LogCpuState() that dumps the
-// active CFSR fields with help strings. This is disabled by default since it
+// Enables extended logging in pw::cpu_exception::LogCpuState() and
+// pw::cpu_exception::cortex_m::LogExceptionAnalysis() that dumps the active
+// CFSR fields with help strings. This is disabled by default since it
// increases the binary size by >1.5KB when using plain-text logs, or ~460
// Bytes when using tokenized logging. It's useful to enable this for device
// bringup until your application has an end-to-end crash reporting solution.
diff --git a/pw_cpu_exception_cortex_m/support.cc b/pw_cpu_exception_cortex_m/support.cc
new file mode 100644
index 0000000..719ac5b
--- /dev/null
+++ b/pw_cpu_exception_cortex_m/support.cc
@@ -0,0 +1,145 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_cpu_exception/support.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <span>
+
+#include "pw_cpu_exception_cortex_m/cpu_state.h"
+#include "pw_cpu_exception_cortex_m/util.h"
+#include "pw_cpu_exception_cortex_m_private/config.h"
+#include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
+#include "pw_log/log.h"
+#include "pw_preprocessor/arch.h"
+#include "pw_string/string_builder.h"
+
+namespace pw::cpu_exception {
+
+std::span<const uint8_t> RawFaultingCpuState(
+ const pw_cpu_exception_State& cpu_state) {
+ return std::span(reinterpret_cast<const uint8_t*>(&cpu_state),
+ sizeof(cpu_state));
+}
+
+// Using this function adds approximately 100 bytes to binary size.
+void ToString(const pw_cpu_exception_State& cpu_state,
+ const std::span<char>& dest) {
+ StringBuilder builder(dest);
+ const cortex_m::ExceptionRegisters& base = cpu_state.base;
+ const cortex_m::ExtraRegisters& extended = cpu_state.extended;
+
+#define _PW_FORMAT_REGISTER(state_section, name) \
+ builder.Format("%s=0x%08" PRIx32 "\n", #name, state_section.name)
+
+ // Other registers.
+ if (base.pc != cortex_m::kUndefinedPcLrOrPsrRegValue) {
+ _PW_FORMAT_REGISTER(base, pc);
+ }
+ if (base.lr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
+ _PW_FORMAT_REGISTER(base, lr);
+ }
+ if (base.psr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
+ _PW_FORMAT_REGISTER(base, psr);
+ }
+ _PW_FORMAT_REGISTER(extended, msp);
+ _PW_FORMAT_REGISTER(extended, psp);
+ _PW_FORMAT_REGISTER(extended, exc_return);
+#if _PW_ARCH_ARM_V8M_MAINLINE
+ _PW_FORMAT_REGISTER(extended, msplim);
+ _PW_FORMAT_REGISTER(extended, psplim);
+#endif // _PW_ARCH_ARM_V8M_MAINLINE
+ _PW_FORMAT_REGISTER(extended, cfsr);
+ _PW_FORMAT_REGISTER(extended, mmfar);
+ _PW_FORMAT_REGISTER(extended, bfar);
+ _PW_FORMAT_REGISTER(extended, icsr);
+ _PW_FORMAT_REGISTER(extended, hfsr);
+ _PW_FORMAT_REGISTER(extended, shcsr);
+ _PW_FORMAT_REGISTER(extended, control);
+
+ // General purpose registers.
+ _PW_FORMAT_REGISTER(base, r0);
+ _PW_FORMAT_REGISTER(base, r1);
+ _PW_FORMAT_REGISTER(base, r2);
+ _PW_FORMAT_REGISTER(base, r3);
+ _PW_FORMAT_REGISTER(extended, r4);
+ _PW_FORMAT_REGISTER(extended, r5);
+ _PW_FORMAT_REGISTER(extended, r6);
+ _PW_FORMAT_REGISTER(extended, r7);
+ _PW_FORMAT_REGISTER(extended, r8);
+ _PW_FORMAT_REGISTER(extended, r9);
+ _PW_FORMAT_REGISTER(extended, r10);
+ _PW_FORMAT_REGISTER(extended, r11);
+ _PW_FORMAT_REGISTER(base, r12);
+
+#undef _PW_FORMAT_REGISTER
+}
+
+// Using this function adds approximately 100 bytes to binary size.
+void LogCpuState(const pw_cpu_exception_State& cpu_state) {
+ const cortex_m::ExceptionRegisters& base = cpu_state.base;
+ const cortex_m::ExtraRegisters& extended = cpu_state.extended;
+
+ cortex_m::LogExceptionAnalysis(cpu_state);
+
+ PW_LOG_INFO("All captured CPU registers:");
+
+#define _PW_LOG_REGISTER(state_section, name) \
+ PW_LOG_INFO(" %-10s 0x%08" PRIx32, #name, state_section.name)
+
+ // Other registers.
+ if (base.pc != cortex_m::kUndefinedPcLrOrPsrRegValue) {
+ _PW_LOG_REGISTER(base, pc);
+ }
+ if (base.lr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
+ _PW_LOG_REGISTER(base, lr);
+ }
+ if (base.psr != cortex_m::kUndefinedPcLrOrPsrRegValue) {
+ _PW_LOG_REGISTER(base, psr);
+ }
+ _PW_LOG_REGISTER(extended, msp);
+ _PW_LOG_REGISTER(extended, psp);
+ _PW_LOG_REGISTER(extended, exc_return);
+#if _PW_ARCH_ARM_V8M_MAINLINE
+ _PW_LOG_REGISTER(extended, msplim);
+ _PW_LOG_REGISTER(extended, psplim);
+#endif // _PW_ARCH_ARM_V8M_MAINLINE
+ _PW_LOG_REGISTER(extended, cfsr);
+ _PW_LOG_REGISTER(extended, mmfar);
+ _PW_LOG_REGISTER(extended, bfar);
+ _PW_LOG_REGISTER(extended, icsr);
+ _PW_LOG_REGISTER(extended, hfsr);
+ _PW_LOG_REGISTER(extended, shcsr);
+ _PW_LOG_REGISTER(extended, control);
+
+ // General purpose registers.
+ _PW_LOG_REGISTER(base, r0);
+ _PW_LOG_REGISTER(base, r1);
+ _PW_LOG_REGISTER(base, r2);
+ _PW_LOG_REGISTER(base, r3);
+ _PW_LOG_REGISTER(extended, r4);
+ _PW_LOG_REGISTER(extended, r5);
+ _PW_LOG_REGISTER(extended, r6);
+ _PW_LOG_REGISTER(extended, r7);
+ _PW_LOG_REGISTER(extended, r8);
+ _PW_LOG_REGISTER(extended, r9);
+ _PW_LOG_REGISTER(extended, r10);
+ _PW_LOG_REGISTER(extended, r11);
+ _PW_LOG_REGISTER(base, r12);
+
+#undef _PW_LOG_REGISTER
+}
+
+} // namespace pw::cpu_exception
diff --git a/pw_cpu_exception_cortex_m/util.cc b/pw_cpu_exception_cortex_m/util.cc
new file mode 100644
index 0000000..ff817b3
--- /dev/null
+++ b/pw_cpu_exception_cortex_m/util.cc
@@ -0,0 +1,153 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_cpu_exception_cortex_m/util.h"
+
+#include <cinttypes>
+
+#include "pw_cpu_exception_cortex_m/cpu_state.h"
+#include "pw_cpu_exception_cortex_m_private/config.h"
+#include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
+#include "pw_log/log.h"
+#include "pw_preprocessor/arch.h"
+
+namespace pw::cpu_exception::cortex_m {
+namespace {
+
+[[maybe_unused]] void LogCfsrAnalysis(const uint32_t cfsr) {
+ if (cfsr == 0) {
+ return;
+ }
+
+ PW_LOG_INFO("Active CFSR fields:");
+
+ // Memory managment fault fields.
+ if (cfsr & kCfsrIaccviolMask) {
+ PW_LOG_ERROR(" IACCVIOL: MPU violation on instruction fetch");
+ }
+ if (cfsr & kCfsrDaccviolMask) {
+ PW_LOG_ERROR(" DACCVIOL: MPU violation on memory read/write");
+ }
+ if (cfsr & kCfsrMunstkerrMask) {
+ PW_LOG_ERROR(" MUNSTKERR: 'MPU violation on exception return");
+ }
+ if (cfsr & kCfsrMstkerrMask) {
+ PW_LOG_ERROR(" MSTKERR: MPU violation on exception entry");
+ }
+ if (cfsr & kCfsrMlsperrMask) {
+ PW_LOG_ERROR(" MLSPERR: MPU violation on lazy FPU state preservation");
+ }
+ if (cfsr & kCfsrMmarvalidMask) {
+ PW_LOG_ERROR(" MMARVALID: MMFAR register is valid");
+ }
+
+ // Bus fault fields.
+ if (cfsr & kCfsrIbuserrMask) {
+ PW_LOG_ERROR(" IBUSERR: Bus fault on instruction fetch");
+ }
+ if (cfsr & kCfsrPreciserrMask) {
+ PW_LOG_ERROR(" PRECISERR: Precise bus fault");
+ }
+ if (cfsr & kCfsrImpreciserrMask) {
+ PW_LOG_ERROR(" IMPRECISERR: Imprecise bus fault");
+ }
+ if (cfsr & kCfsrUnstkerrMask) {
+ PW_LOG_ERROR(" UNSTKERR: Derived bus fault on exception context save");
+ }
+ if (cfsr & kCfsrStkerrMask) {
+ PW_LOG_ERROR(" STKERR: Derived bus fault on exception context restore");
+ }
+ if (cfsr & kCfsrLsperrMask) {
+ PW_LOG_ERROR(" LSPERR: Derived bus fault on lazy FPU state preservation");
+ }
+ if (cfsr & kCfsrBfarvalidMask) {
+ PW_LOG_ERROR(" BFARVALID: BFAR register is valid");
+ }
+
+ // Usage fault fields.
+ if (cfsr & kCfsrUndefinstrMask) {
+ PW_LOG_ERROR(" UNDEFINSTR: Encountered invalid instruction");
+ }
+ if (cfsr & kCfsrInvstateMask) {
+ PW_LOG_ERROR(
+ " INVSTATE: Attempted to execute an instruction with an invalid "
+ "Execution Program Status Register (EPSR) value");
+ }
+ if (cfsr & kCfsrInvpcMask) {
+ PW_LOG_ERROR(" INVPC: Program Counter (PC) is not legal");
+ }
+ if (cfsr & kCfsrNocpMask) {
+ PW_LOG_ERROR(" NOCP: Coprocessor disabled or not present");
+ }
+ if (cfsr & kCfsrUnalignedMask) {
+ PW_LOG_ERROR(" UNALIGNED: Unaligned memory access");
+ }
+ if (cfsr & kCfsrDivbyzeroMask) {
+ PW_LOG_ERROR(" DIVBYZERO: Division by zero");
+ }
+#if _PW_ARCH_ARM_V8M_MAINLINE
+ if (cfsr & kCfsrStkofMask) {
+ PW_LOG_ERROR(" STKOF: Stack overflowed");
+ }
+#endif // _PW_ARCH_ARM_V8M_MAINLINE
+}
+
+} // namespace
+
+void LogExceptionAnalysis(const pw_cpu_exception_State& cpu_state) {
+ // This provides a high-level assessment of the cause of the exception.
+ // These conditionals are ordered by priority to ensure the most critical
+ // issues are highlighted first. These are not mutually exclusive; a bus fault
+ // could occur during the handling of a MPU violation, causing a nested fault.
+ if (cpu_state.extended.hfsr & kHfsrForcedMask) {
+ PW_LOG_CRITICAL("Encountered a nested CPU fault (See active CFSR fields)");
+ }
+#if _PW_ARCH_ARM_V8M_MAINLINE
+ if (cpu_state.extended.cfsr & kCfsrStkofMask) {
+ if (cpu_state.extended.exc_return & kExcReturnStackMask) {
+ PW_LOG_CRITICAL("Encountered stack overflow in thread mode");
+ } else {
+ PW_LOG_CRITICAL("Encountered main (interrupt handler) stack overflow");
+ }
+ }
+#endif // _PW_ARCH_ARM_V8M_MAINLINE
+ if (cpu_state.extended.cfsr & kCfsrMemFaultMask) {
+ if (cpu_state.extended.cfsr & kCfsrMmarvalidMask) {
+ PW_LOG_CRITICAL(
+ "Encountered Memory Protection Unit (MPU) violation at 0x%08" PRIx32,
+ cpu_state.extended.mmfar);
+ } else {
+ PW_LOG_CRITICAL("Encountered Memory Protection Unit (MPU) violation");
+ }
+ }
+ if (cpu_state.extended.cfsr & kCfsrBusFaultMask) {
+ if (cpu_state.extended.cfsr & kCfsrBfarvalidMask) {
+ PW_LOG_CRITICAL("Encountered bus fault at 0x%08" PRIx32,
+ cpu_state.extended.bfar);
+ } else {
+ PW_LOG_CRITICAL("Encountered bus fault");
+ }
+ }
+ if (cpu_state.extended.cfsr & kCfsrUsageFaultMask) {
+ PW_LOG_CRITICAL("Encountered usage fault (See active CFSR fields)");
+ }
+ if ((cpu_state.extended.icsr & kIcsrVectactiveMask) == kNmiIsrNum) {
+ PW_LOG_INFO("Encountered non-maskable interrupt (NMI)");
+ }
+#if PW_CPU_EXCEPTION_CORTEX_M_EXTENDED_CFSR_DUMP
+ LogCfsrAnalysis(cpu_state.extended.cfsr);
+#endif // PW_CPU_EXCEPTION_CORTEX_M_EXTENDED_CFSR_DUMP
+}
+
+} // namespace pw::cpu_exception::cortex_m