2 # Copyright 2020 The Pigweed Authors
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 # use this file except in compliance with the License. You may obtain a copy of
8 # https://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations under
15 """Tests dumped Cortex-M CPU state."""
20 from pw_protobuf_compiler import python_protos
21 from pw_cli import env
22 from pw_cpu_exception_cortex_m import exception_analyzer, cortex_m_constants
24 CPU_STATE_PROTO_PATH = os.path.join(
25 env.pigweed_environment().PW_ROOT, #pylint: disable=no-member
26 'pw_cpu_exception_cortex_m',
27 'pw_cpu_exception_cortex_m_protos',
30 cpu_state_pb2 = python_protos.compile_and_import_file(CPU_STATE_PROTO_PATH)
32 # pylint: disable=protected-access
35 class BasicFaultTest(unittest.TestCase):
36 """Test basic fault analysis functions."""
37 def test_empty_state(self):
38 """Ensure an empty CPU state proto doesn't indicate an active fault."""
39 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
40 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
42 self.assertFalse(cpu_state_info.is_fault_active())
44 def test_cfsr_fault(self):
45 """Ensure a fault is active if CFSR bits are set."""
46 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
47 cpu_state_proto.cfsr = (
48 cortex_m_constants.PW_CORTEX_M_CFSR_STKOF_MASK
49 | cortex_m_constants.PW_CORTEX_M_CFSR_MUNSTKERR_MASK)
50 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
52 self.assertTrue(cpu_state_info.is_fault_active())
54 def test_icsr_fault(self):
55 """Ensure a fault is active if ICSR says the handler is active."""
56 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
57 cpu_state_proto.icsr = (
58 cortex_m_constants.PW_CORTEX_M_HARD_FAULT_ISR_NUM)
59 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
61 self.assertTrue(cpu_state_info.is_fault_active())
63 def test_cfsr_fields(self):
64 """Ensure correct fields are returned when CFSR bits are set."""
65 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
66 cpu_state_proto.cfsr = (
67 cortex_m_constants.PW_CORTEX_M_CFSR_STKOF_MASK
68 | cortex_m_constants.PW_CORTEX_M_CFSR_MUNSTKERR_MASK)
69 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
72 field.name for field in cpu_state_info.active_cfsr_fields()
74 self.assertEqual(len(active_fields), 2)
75 self.assertIn('STKOF', active_fields)
76 self.assertIn('MUNSTKERR', active_fields)
79 class ExceptionCauseTest(unittest.TestCase):
80 """Test exception cause analysis."""
81 def test_empty_cpu_state(self):
82 """Ensure empty CPU state has no known cause."""
83 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
84 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
86 self.assertEqual(cpu_state_info.exception_cause(), 'unknown exception')
88 def test_unknown_exception(self):
89 """Ensure CPU state with insufficient info has no known cause."""
90 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
91 # Set CFSR to a valid value.
92 cpu_state_proto.cfsr = 0
93 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
95 self.assertEqual(cpu_state_info.exception_cause(), 'unknown exception')
97 def test_single_usage_fault(self):
98 """Ensure usage faults are properly identified."""
99 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
100 cpu_state_proto.cfsr = cortex_m_constants.PW_CORTEX_M_CFSR_STKOF_MASK
101 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
103 self.assertEqual(cpu_state_info.exception_cause(),
104 'usage fault [STKOF]')
106 def test_single_usage_fault_without_fields(self):
107 """Ensure disabling show_active_cfsr_fields hides field names."""
108 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
109 cpu_state_proto.cfsr = cortex_m_constants.PW_CORTEX_M_CFSR_STKOF_MASK
110 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
112 self.assertEqual(cpu_state_info.exception_cause(False), 'usage fault')
114 def test_multiple_faults(self):
115 """Ensure multiple CFSR bits are identified and reported."""
116 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
117 cpu_state_proto.cfsr = (
118 cortex_m_constants.PW_CORTEX_M_CFSR_STKOF_MASK
119 | cortex_m_constants.PW_CORTEX_M_CFSR_UNSTKERR_MASK)
120 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
122 self.assertEqual(cpu_state_info.exception_cause(),
123 'usage fault, bus fault [UNSTKERR] [STKOF]')
125 def test_mmfar_missing(self):
126 """Ensure if mmfar is valid but missing it is handled safely."""
127 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
128 cpu_state_proto.cfsr = (
129 cortex_m_constants.PW_CORTEX_M_CFSR_MUNSTKERR_MASK
130 | cortex_m_constants.PW_CORTEX_M_CFSR_MMARVALID_MASK)
131 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
133 self.assertEqual(cpu_state_info.exception_cause(False),
134 'memory management fault at ???')
136 def test_mmfar_valid(self):
137 """Validate output format of valid MMFAR."""
138 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
139 cpu_state_proto.cfsr = (
140 cortex_m_constants.PW_CORTEX_M_CFSR_MUNSTKERR_MASK
141 | cortex_m_constants.PW_CORTEX_M_CFSR_MMARVALID_MASK)
142 cpu_state_proto.mmfar = 0x722470e4
143 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
145 self.assertEqual(cpu_state_info.exception_cause(False),
146 'memory management fault at 0x722470e4')
148 def test_imprecise_bus_fault(self):
149 """Check that imprecise bus faults are identified correctly."""
150 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
151 cpu_state_proto.cfsr = (
152 cortex_m_constants.PW_CORTEX_M_CFSR_IMPRECISERR_MASK
153 | cortex_m_constants.PW_CORTEX_M_CFSR_IBUSERR_MASK)
154 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
156 self.assertEqual(cpu_state_info.exception_cause(False),
157 'imprecise bus fault')
160 class TextDumpTest(unittest.TestCase):
161 """Test larger state dumps."""
162 def test_registers(self):
163 """Validate output of general register dumps."""
164 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
165 cpu_state_proto.pc = 0xdfadd966
166 cpu_state_proto.mmfar = 0xaf2ea98a
167 cpu_state_proto.r0 = 0xf3b235b1
168 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
170 expected_dump = '\n'.join((
175 self.assertEqual(cpu_state_info.dump_registers(), expected_dump)
177 def test_dump_no_cfsr(self):
178 """Validate basic CPU state dump."""
179 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
180 cpu_state_proto.pc = 0xd2603058
181 cpu_state_proto.mmfar = 0x8e4eb9a2
182 cpu_state_proto.r0 = 0xdb5e7168
183 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
185 expected_dump = '\n'.join((
186 'Exception caused by a unknown exception.',
188 'No active Crash Fault Status Register (CFSR) fields.',
195 self.assertEqual(str(cpu_state_info), expected_dump)
197 def test_dump_with_cfsr(self):
198 """Validate CPU state dump with CFSR bits set is formatted correctly."""
199 cpu_state_proto = cpu_state_pb2.ArmV7mCpuState()
200 cpu_state_proto.cfsr = (
201 cortex_m_constants.PW_CORTEX_M_CFSR_PRECISERR_MASK
202 | cortex_m_constants.PW_CORTEX_M_CFSR_BFARVALID_MASK)
203 cpu_state_proto.pc = 0xd2603058
204 cpu_state_proto.bfar = 0xdeadbeef
205 cpu_state_proto.mmfar = 0x8e4eb9a2
206 cpu_state_proto.r0 = 0xdb5e7168
207 cpu_state_info = exception_analyzer.CortexMExceptionAnalyzer(
209 expected_dump = '\n'.join((
210 'Exception caused by a bus fault at 0xdeadbeef.',
212 'Active Crash Fault Status Register (CFSR) fields:',
213 'PRECISERR Precise bus fault.',
214 'BFARVALID BFAR is valid.',
223 self.assertEqual(str(cpu_state_info), expected_dump)
226 if __name__ == '__main__':