#
# 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

	# 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
	arm 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
}