Cortex-M3 vector_catch testing support
authorDavid Brownell <dbrownell@users.sourceforge.net>
Wed, 20 Jan 2010 19:07:42 +0000 (11:07 -0800)
committerDavid Brownell <dbrownell@users.sourceforge.net>
Wed, 20 Jan 2010 19:07:42 +0000 (11:07 -0800)
The "cm3-ftest.cfg" can be used to verify that OpenOCD handles
certain faults correctly:

 - Test #1: it ignores faults that it wasn't told to catch
 - Test #2: if vector_catch is told to catch, it catches

The "fault.c" generates ASM code to trigger faults, while the
config script loads and runs pre-compiled code.

This covers most, but not all, of the vector_catch options.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
testing/examples/cortex/cm3-ftest.cfg [new file with mode: 0644]
testing/examples/cortex/fault.c [new file with mode: 0644]

diff --git a/testing/examples/cortex/cm3-ftest.cfg b/testing/examples/cortex/cm3-ftest.cfg
new file mode 100644 (file)
index 0000000..2dae249
--- /dev/null
@@ -0,0 +1,143 @@
+#
+# For each named Cortex-M3 vector_catch flag VECTOR ...
+#              bus_err         state_err
+#              chk_err         nocp_err
+#              mm_err          reset
+#
+# BUT NYET hard_err, int_err (their test cases don't yet work) ...
+#
+# Do the following:
+#
+#  - Test #1:  verify that OpenOCD ignores exceptions by default
+#     + l_VECTOR (loads testcase to RAM)
+#     + fault triggers loop-to-self exception "handler"
+#     + "halt"
+#     + observe fault "handling" -- loop-to-self from load_and_run (below)
+#
+#  - Test #2:  verify that "vector_catch" makes OpenOCD stops ignoring them
+#     + cortex_m3 vector_catch none
+#     + cortex_m3 vector_catch VECTOR
+#     + l_VECTOR (loads testcase to RAM)
+#     + fault triggers vector catch hardware
+#     + observe OpenOCD entering debug state with no assistance
+#
+# NOTE "reset" includes the NVIC, so that test case gets its reset vector
+# from the flash, not from the vector table set up here.  Which means that
+# for that vector_catch option, the Test #1 (above) "observe" step won't
+# use the SRAM address.
+#
+
+# we can fully automate test #2
+proc vector_test {tag} {
+       halt
+       # REVISIT -- annoying, we'd like to scrap vector_catch output
+       cortex_m3 vector_catch none
+       cortex_m3 vector_catch $tag
+       eval "l_$tag"
+}
+
+#
+# Load and start one vector_catch test case.
+#
+# name -- tag for the vector_catch flag being tested
+# halfwords -- array of instructions (some wide, some narrow)
+# n_instr -- how many instructions are in $halfwords
+#
+proc load_and_run { name halfwords n_instr } {
+       reset halt
+
+       # Load code at beginning of SRAM.
+       echo "# code to trigger $name vector"
+       set addr 0x20000000
+
+       # ocd_array2mem should be faster, though we'd need to
+       # compute the resulting $addr ourselves
+       foreach opcode $halfwords {
+               mwh $addr $opcode
+               incr addr 2
+       }
+
+       # create default loop-to-self at $addr ... it serves as
+       # (a) "main loop" on error
+       # (b) handler for all exceptions that get triggered
+       mwh $addr 0xe7fe
+
+       # disassemble, as sanity check and what's-happening trace
+       cortex_m3 disassemble 0x20000000 [expr 1 + $n_instr ]
+
+       # Assume that block of code is at most 16 halfwords long.
+       # Create a basic table of loop-to-self exception handlers.
+       mww 0x20000020 $addr 16
+       # Store its address in VTOR
+       mww 0xe000ed08 0x20000020
+       # Use SHCSR to ensure nothing escalates to a HardFault
+       mww 0xe000ed24 0x00070000
+
+       # now start, trigering the $name vector catch logic
+       resume 0x20000000
+}
+
+#proc l_hard_err {} {
+#      IMPLEMENT ME
+#      FORCED -- escalate something to HardFault
+#}
+
+#proc l_int_err {} {
+#      IMPLEMENT ME
+#      STKERR -- exception stack BusFault
+#}
+
+# BusFault, escalates to HardFault
+proc l_bus_err {} {
+       # PRECISERR -- assume less than 512 MBytes of SRAM
+       load_and_run bus_err {
+               0xf06f 0x4040
+               0x7800
+       } 2
+}
+
+# UsageFault, escalates to HardFault
+proc l_state_err {} {
+       # UNDEFINSTR -- issue architecturally undefined instruction
+       load_and_run state_err {
+               0xde00
+       } 1
+}
+
+# UsageFault, escalates to HardFault
+proc l_chk_err {} {
+       # UNALIGNED -- LDM through unaligned pointer
+       load_and_run chk_err {
+               0xf04f 0x0001
+               0xe890 0x0006
+       } 2
+}
+
+# UsageFault, escalates to HardFault
+proc l_nocp_err {} {
+       # NOCP -- issue cp14 DCC instruction
+       load_and_run nocp_err {
+               0xee10 0x0e15
+       } 1
+}
+
+# MemManage, escalates to HardFault
+proc l_mm_err {} {
+       # IACCVIOL -- instruction fetch from an XN region
+       load_and_run mm_err {
+               0xf04f 0x4060
+               0x4687
+       } 2
+}
+
+proc l_reset {} {
+       # issue SYSRESETREQ via AIRCR
+       load_and_run reset {
+               0xf04f 0x0104
+               0xf2c0 0x51fa
+               0xf44f 0x406d
+               0xf100 0x000c
+               0xf2ce 0x0000
+               0x6001
+       } 6
+}
diff --git a/testing/examples/cortex/fault.c b/testing/examples/cortex/fault.c
new file mode 100644 (file)
index 0000000..9a5fe19
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * COMPILE:  arm-none-eabi-gcc -mthumb -march=armv7-m ...
+ *     ... plus, provide at least a default exception vector table.
+ *
+ * RUN:  this is best run from SRAM.  It starts at main() then triggers
+ * a fault before more than a handful of instructions have executed.
+ * Run each test case in two modes:
+ *
+ * (1) Faults caught on the Cortex-M3.  Default handlers are usually
+ *     loop-to-self NOPs, so a debugger won't notice faults until they
+ *     halt the core and examine xSPR and other registers.
+ *
+ *     To verify the fault triggered, issue "halt" from OpenOCD; you
+ *     should be told about the fault and (some of) its details.
+ *     Then it's time to run the next test.
+ *
+ *     NOTE however that "reset" will restart everything; verify that
+ *     case by observing your reset handler doing its normal work.
+ *
+ * (2) Faults intercepted by OpenOCD "vector_catch ..." commands.
+ *
+ *     OpenOCD should tell you about the fault, and show the same
+ *     details, without your "halt" command.
+ *
+ * Someday, a fancy version of this code could provide a vector table and
+ * fault handlers which use semihosting (when that works on Cortex-M3) to
+ * report what happened, again without needing a "halt" command.
+ */
+
+
+/* These symbols match the OpenOCD "cortex_m3 vector_catch" bit names. */
+enum vc_case {
+       hard_err,
+       int_err,
+       bus_err,
+       state_err,
+       chk_err,
+       nocp_err,
+       mm_err,
+       reset,
+};
+
+/* REVISIT come up with a way to avoid recompiling, maybe:
+ *  - write it in RAM before starting
+ *  - compiled-in BKPT, manual patch of r0, then resume
+ *  - ...
+ */
+
+#ifndef VC_ID
+#warning "no VC_ID ... using reset"
+#define VC_ID reset
+#endif
+
+int main(void) __attribute__ ((externally_visible, noreturn));
+
+/*
+ * Trigger various Cortex-M3 faults to verify that OpenOCD behaves OK
+ * in terms of its vector_catch handling.
+ *
+ * Fault handling should be left entirely up to the application code
+ * UNLESS a "vector_catch" command tells OpenOCD to intercept a fault.
+ *
+ * See ARMv7-M architecure spec table B1-9 for the list of faults and
+ * their mappings to the vector catch bits.
+ */
+int main(void)
+{
+       /* One test case for each vector catch bit.  We're not doing
+        * hardware testing; so it doesn't matter when some DEMCR bits
+        * could apply in multiple ways.
+        */
+       switch (VC_ID) {
+
+       /* "cortex_m3 vector_catch hard_err" */
+       case hard_err:
+               /* FORCED - Fault escalation */
+
+               /* FIXME code this */
+               break;
+
+       /* "cortex_m3 vector_catch int_err" */
+       case int_err:
+               /* STKERR -- Exception stack BusFault */
+
+               /* FIXME code this */
+               break;
+
+       /* "cortex_m3 vector_catch bus_err" */
+       case bus_err:
+               /* PRECISERR -- precise data bus read
+                * Here we assume a Cortex-M3 with 512 MBytes SRAM is very
+                * unlikely, so the last SRAM byte isn't a valid address.
+                */
+               __asm__ volatile(
+                       "mov r0, #0x3fffffff\n"
+                       "ldrb r0, [r0]\n"
+                       );
+               break;
+
+       /* "cortex_m3 vector_catch state_err" */
+       case state_err:
+               /* UNDEFINSTR -- architectural undefined instruction */
+               __asm__ volatile(".hword 0xde00");
+               break;
+
+       /* "cortex_m3 vector_catch chk_err" */
+       case chk_err:
+               /* UNALIGNED ldm */
+               __asm__ volatile(
+                       "mov r0, #1\n"
+                       "ldm r0, {r1, r2}\n"
+                       );
+               break;
+
+       /* "cortex_m3 vector_catch nocp_err" */
+       case nocp_err:
+               /* NOCP ... Cortex-M3 has no coprocessors (like CP14 DCC),
+                * but these instructions are allowed by ARMv7-M.
+                */
+               __asm__ volatile("mrc p14, 0, r0, c0, c5, 0");
+               break;
+
+       /* "cortex_m3 vector_catch mm_err" */
+       case mm_err:
+               /* IACCVIOL -- instruction fetch from an XN region */
+               __asm__ volatile(
+                       "mov r0, #0xe0000000\n"
+                       "mov pc, r0\n"
+                       );
+               break;
+
+       /* "cortex_m3 vector_catch reset" */
+       case reset:
+               __asm__ volatile(
+                       /* r1 = SYSRESETREQ */
+                       "mov r1, #0x0004\n"
+                       /* r1 |= VECTKEY */
+                       "movt r1, #0x05fa\n"
+                       /* r0 = &AIRCR */
+                       "mov r0, #0xed00\n"
+                       "add r0, #0xc\n"
+                       "movt r0, #0xe000\n"
+                       /* AIRCR = ... */
+                       "str r1, [r0, #0]\n"
+                       );
+               break;
+       }
+
+       /* don't return */
+       while (1)
+               continue;
+}

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)