// 
// debug_hndlr.S -- default Xtensa debug exception handler
//
// $Id: //depot/rel/Foxhill/dot.8/Xtensa/OS/hal/debug_hndlr.S#1 $

// Copyright (c) 2003-2010 Tensilica Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <xtensa/coreasm.h>
#include <xtensa/config/system.h>

#if XCHAL_HAVE_DEBUG && XCHAL_HAVE_EXCEPTIONS

	/*
	 *  Default debug exception handler.
	 *
	 *  Note that the debug exception vector must save a3
	 *  in EXCSAVE+XCHAL_DEBUGLEVEL before jumping here.
	 *
	 *  This handler is used when no debugger is present.
	 *  The end result of executing this default handler
	 *  is as if no debug exception had occurred, eg. as if
	 *  the core was running at PS.INTLEVEL >= DEBUGLEVEL.
	 *
	 *  Because the debug exception vector might get
	 *  placed in ROM, and be expected to work regardless
	 *  of what executable image or OS is running in RAM,
	 *  we're very careful not to use any RAM here.
	 *  We don't know what RAM we can safely use.
	 *  This tricky part to accomplishing this feat
	 *  is to use only *one* register (a3, which was
	 *  saved in EXCSAVE+XCHAL_DEBUGLEVEL), because we don't
	 *  have RAM in which to safely store other regs.
	 *
	 *  A real debugger application would normally
	 *  have some kind of conventions, or special
	 *  hardware support, to have its own RAM workspace
	 *  in which to save context and do real work
	 *  in this handler.
	 */


#if XSHAL_DEBUG_VECTOR_ISROM
	//  Debug exception vector is in ROM, so place the handler
	//  in ROM also.  Otherwise running different executables
	//  with that ROM will not work because the handler would
	//  likely not be there or be at the wrong address.
	//
	.section	.srom.text, "ax"
#else
	//  Debug exception vector is in RAM, so we can safely
	//  place the handler in RAM as well.
	//
	.text
#endif

	.global xthal_debugexc_defhndlr_nw
	.align 4
xthal_debugexc_defhndlr_nw:
	rsr.debugcause	a3		// get cause of debug exception

	//  Check for possible debug causes, in priority order.
	//  We only handle the highest priority condition present.
	//  (If there are multiple conditions, the lower priority
	//   condition(s) will normally trigger upon return from
	//   this exception handler.)

	bbci.l	a3, DEBUGCAUSE_ICOUNT_SHIFT, 1f	// ICOUNT trap?
	movi	a3, 0
	wsr.icount a3				// clear ICOUNT
	j	3f

/*
 *  Ensure that we have IBREAKs, otherwise the IBREAKENABLE
 *  special register is not there:
 */
#if XCHAL_NUM_IBREAK > 0
1:	bbci.l	a3, DEBUGCAUSE_IBREAK_SHIFT, 1f	// IBREAK match?
	movi	a3, 0
	wsr.ibreakenable a3			// disable IBREAK traps
	j	3f
#endif

/*  Also check for DBREAK registers:  */
#if XCHAL_NUM_DBREAK > 0
1:	bbci.l	a3, DEBUGCAUSE_DBREAK_SHIFT, 1f	// DBREAK match?
	movi	a3, 0
	wsr.dbreakc0 a3				// disable DBREAK register 0
# if XCHAL_NUM_DBREAK > 1
	wsr.dbreakc1 a3				// disable DBREAK register 1
# endif
	j	3f
#endif

1:	bbci.l	a3, DEBUGCAUSE_BREAK_SHIFT, 1f	// BREAK instruction?
	//readsr epc XCHAL_DEBUGLEVEL a3	// get PC pointing to BREAK
	//l8ui	a3, a3, 1			// get first 4-bit operand of BREAK (in 2nd byte)
	//extui	a3, a3, (XCHAL_HAVE_BE*4), 4	// pos depends on endianness
	//bnei	a3, 1, 3f			// is it a BREAK 1,x instruction?
	readsr	epc XCHAL_DEBUGLEVEL a3		// get PC pointing to BREAK
	addi	a3, a3, 3			// skip BREAK instruction
	writesr	epc XCHAL_DEBUGLEVEL a3		// update PC
	j	3f

1:	bbci.l	a3, DEBUGCAUSE_BREAKN_SHIFT, 1f	// BREAK.N instruction?
	readsr	epc XCHAL_DEBUGLEVEL a3		// get PC pointing to BREAK
	addi	a3, a3, 2			// skip BREAK.N instruction
	writesr	epc XCHAL_DEBUGLEVEL a3		// update PC
	j	3f

1:	bbci.l	a3, DEBUGCAUSE_DEBUGINT_SHIFT, 1f	// debug interrupt?
	//  Nothing to do...
	j	3f

1:	//  Unknown debug case?  ignore

3:	readsr	excsave XCHAL_DEBUGLEVEL a3	// restore a3
	rfi	XCHAL_DEBUGLEVEL		// return from debug exception

	.size	xthal_debugexc_defhndlr_nw, . - xthal_debugexc_defhndlr_nw


#if XSHAL_DEBUG_VECTOR_ISROM
	.text		// in case this gets included by something else
#endif

#endif /* XCHAL_HAVE_DEBUG */

