Cortex-M3 vector_catch testing support
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>
This commit is contained in:
parent
22d25e6921
commit
2a0c9b08d7
|
@ -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
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue