blob: 9e2b2654d303c38fb4cd585548938be9d0d59067 [file] [log] [blame]
//
// debug.c - debug related constants and functions
//
// $Id: //depot/rel/Foxhill/dot.8/Xtensa/OS/hal/debug.c#1 $
// Copyright (c) 2002 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/hal.h>
#include <xtensa/config/core.h>
/* 1 if debug option configured, 0 if not: */
const int Xthal_debug_configured = XCHAL_HAVE_DEBUG;
/* Number of instruction and data break registers: */
const int Xthal_num_ibreak = XCHAL_NUM_IBREAK;
const int Xthal_num_dbreak = XCHAL_NUM_DBREAK;
#ifdef INCLUDE_DEPRECATED_HAL_DEBUG_CODE
/* This array is incorrect: */
const unsigned short Xthal_ill_inst_16[16] =
{
#if XCHAL_HAVE_BE
0xfd0f, 0xfd1f, 0xfd2f, 0xfd3f,
0xfd4f, 0xfd5f, 0xfd6f, 0xfd7f,
0xfd8f, 0xfd9f, 0xfdaf, 0xfdbf,
0xfdcf, 0xfddf, 0xfdef, 0xfdff
#else
0xf0fd, 0xf1fd, 0xf2fd, 0xf3fd,
0xf4fd, 0xf5fd, 0xf6fd, 0xf7fd,
0xf8fd, 0xf9fd, 0xfafd, 0xfbfd,
0xfcfd, 0xfdfd, 0xfefd, 0xfffd
#endif
};
#endif /* INCLUDE_DEPRECATED_HAL_DEBUG_CODE */
#undef XTHAL_24_BIT_BREAK
#undef XTHAL_16_BIT_BREAK
#define XTHAL_24_BIT_BREAK 0x80000000
#define XTHAL_16_BIT_BREAK 0x40000000
// set software breakpoint and synchronize cache
unsigned int
xthal_set_soft_break(void *addr)
{
unsigned inst;
int is24bit = (xthal_disassemble_size( (unsigned char *)addr ) == 3);
unsigned int ret_val;
#if XCHAL_HAVE_BE
inst = ((((char *)addr)[0])<<24) +
((((char *)addr)[1])<<16) +
((((char *)addr)[2])<<8);
#else
inst = ((((char *)addr)[0])) +
((((char *)addr)[1])<<8) +
((((char *)addr)[2])<<16);
#endif
#if XCHAL_HAVE_BE
if (is24bit) {
ret_val = XTHAL_24_BIT_BREAK & ((inst>>8)&0xffffff);
((unsigned char *)addr)[0] = 0x00;
((unsigned char *)addr)[1] = 0x04;
((unsigned char *)addr)[2] = 0x00;
} else {
ret_val = XTHAL_16_BIT_BREAK & ((inst>>16)&0xffff);
((unsigned char *)addr)[0] = 0xD2;
((unsigned char *)addr)[1] = 0x0f;
}
#else
if (is24bit) {
ret_val = XTHAL_24_BIT_BREAK & (inst&0xffffff);
((unsigned char *)addr)[0] = 0x00;
((unsigned char *)addr)[1] = 0x40;
((unsigned char *)addr)[2] = 0x00;
} else {
ret_val = XTHAL_16_BIT_BREAK & (inst&0xffff);
((unsigned char *)addr)[0] = 0x2D;
((unsigned char *)addr)[1] = 0xf0;
}
#endif
*((unsigned int *)addr) = inst;
#if XCHAL_DCACHE_IS_WRITEBACK
xthal_dcache_region_writeback((void*)addr, 3);
#endif
#if XCHAL_ICACHE_SIZE > 0
xthal_icache_region_invalidate((void*)addr, 3);
#endif
return ret_val;
}
// remove software breakpoint and synchronize cache
void
xthal_remove_soft_break(void *addr, unsigned int inst)
{
#if XCHAL_HAVE_BE
if (inst&XTHAL_24_BIT_BREAK) {
((unsigned char *)addr)[0] = (inst>>16)&0xff;
((unsigned char *)addr)[1] = (inst>>8)&0xff;
((unsigned char *)addr)[2] = inst&0xff;
} else {
((unsigned char *)addr)[0] = (inst>>8)&0xff;
((unsigned char *)addr)[1] = inst&0xff;
}
#else
((unsigned char *)addr)[0] = inst&0xff;
((unsigned char *)addr)[1] = (inst>>8)&0xff;
if (inst&XTHAL_24_BIT_BREAK)
((unsigned char *)addr)[2] = (inst>>16)&0xff;
#endif
#if XCHAL_DCACHE_IS_WRITEBACK
xthal_dcache_region_writeback((void*)addr, 3);
#endif
#if XCHAL_ICACHE_SIZE > 0
xthal_icache_region_invalidate((void*)addr, 3);
#endif
}
#ifdef INCLUDE_DEPRECATED_HAL_DEBUG_CODE
// return instruction type
unsigned int
xthal_inst_type(void *addr)
{
unsigned int inst_type = 0;
unsigned inst;
// unsigned int inst = *((unsigned int *)addr);
unsigned char op0, op1, op2;
unsigned char i, m, n, r, s, t, z;
#if XCHAL_HAVE_BE
inst = ((((char *)addr)[0])<<24) +
((((char *)addr)[1])<<16) +
((((char *)addr)[2])<<8);
op0 = inst>>28;
op1 = (inst>>12)&0xf;
op2 = (inst>>16)&0xf;
i = (inst>>27)&0x1;
z = (inst>>26)&0x1;
m = (inst>>24)&0x3;
n = (inst>>26)&0x3;
r = (inst>>16)&0xf;
s = (inst>>20)&0xf;
t = (inst>>24)&0xf;
#else
inst = ((((char *)addr)[0])) +
((((char *)addr)[1])<<8) +
((((char *)addr)[2])<<16);
op0 = inst&0xf;
op1 = (inst&0xf0000)>>16;
op2 = (inst&0xf00000)>>20;
i = (inst&0x80)>>7;
z = (inst&0x40)>>6;
m = (inst&0xc0)>>6;
n = (inst&0x30)>>4;
r = (inst&0xf000)>>12;
s = (inst&0xf00)>>8;
t = (inst&0xf0)>4;
#endif
switch (op0) {
case 0x0:
inst_type |= XTHAL_24_BIT_INST;
if ((op1==0)&&(op2==0))
switch (r) {
case 0:
if (m==0x2) {
if (!(n&0x2)) // RET, RETW
inst_type |= XTHAL_RET_INST;
else if (n==0x2) // JX
inst_type |= (XTHAL_JUMP_INST|XTHAL_DEST_REG_INST);
inst_type |= (s<<28);
} else if (m==3) // CALLX
inst_type |= (XTHAL_JUMP_INST|XTHAL_DEST_REG_INST);
inst_type |= (s<<28);
break;
case 0x3:
if (t==0)
switch (s) {
case 0x0: // RFE
inst_type |= XTHAL_RFE_INST;
break;
case 0x1: // RFUE
inst_type |= XTHAL_RFUE_INST;
break;
case 0x4: // RFW
case 0x5:
inst_type |= XTHAL_RFW_INST;
break;
}
else if (t==1) // RFI
inst_type |= XTHAL_RFI_INST;
break;
case 0x4: // BREAK
inst_type |= XTHAL_BREAK_INST;
break;
case 0x5: // SYSCALL
inst_type |= XTHAL_SYSCALL_INST;
break;
}
break;
case 0x5: // CALL
inst_type |= XTHAL_24_BIT_INST;
inst_type |= (XTHAL_JUMP_INST|XTHAL_DEST_REL_INST);
break;
case 0x6: // B
inst_type |= XTHAL_24_BIT_INST;
if (n==0) // J
inst_type |= (XTHAL_JUMP_INST|XTHAL_DEST_REL_INST);
else if ((n==0x1)||(n==0x2))
inst_type |= (XTHAL_BRANCH_INST|XTHAL_DEST_REL_INST);
else {
if (m&0x2)
inst_type |= (XTHAL_BRANCH_INST|XTHAL_DEST_REL_INST);
else if ((m==0x1)&&((r==0x0)||(r==0x1)))
inst_type |= (XTHAL_BRANCH_INST|XTHAL_DEST_REL_INST);
}
break;
case 0x7: // B
inst_type |= XTHAL_24_BIT_INST;
inst_type |= (XTHAL_BRANCH_INST|XTHAL_DEST_REL_INST);
break;
#if XCHAL_HAVE_DENSITY
case 0x8: // L32I.N
case 0x9: // S32I.N
case 0xA: // ADD.N
case 0xb: // ADDI.N
inst_type |= XTHAL_16_BIT_INST;
break;
case 0xc:
inst_type |= XTHAL_16_BIT_INST; // MOVI.N BEQZ.N, BNEZ.N
if (i)
inst_type |= (XTHAL_BRANCH_INST|XTHAL_DEST_REL_INST);
break;
case 0xd: // MOV.N NOP.N
inst_type |= XTHAL_16_BIT_INST;
if (r==0xf)
switch(t) {
case 0x0:
case 0x1:
inst_type |= XTHAL_RET_INST; // RET.N, RETW.N
break;
case 0x2:
inst_type |= XTHAL_BREAK_INST; // BREAK.N
break;
}
break;
#endif /* XCHAL_HAVE_DENSITY */
default:
inst_type |= XTHAL_24_BIT_INST;
}
return inst_type;
}
// returns branch address
unsigned int
xthal_branch_addr(void *addr)
{
unsigned int b_addr = (unsigned int) addr;
unsigned inst;
// unsigned int inst = *((unsigned int *)addr);
int offset;
unsigned int inst_type = xthal_inst_type(addr);
unsigned int inst_type_mask;
#if XCHAL_HAVE_BE
inst = ((((char *)addr)[0])<<24) +
((((char *)addr)[1])<<16) +
((((char *)addr)[2])<<8);
#else
inst = ((((char *)addr)[0])) +
((((char *)addr)[1])<<8) +
((((char *)addr)[2])<<16);
#endif
#if XCHAL_HAVE_DENSITY
inst_type_mask = XTHAL_16_BIT_INST|XTHAL_BRANCH_INST|XTHAL_DEST_REL_INST;
if ((inst_type&inst_type_mask)==inst_type_mask) {
# if XCHAL_HAVE_BE
b_addr += (4+((inst&0x3000000)>>20)+((inst&0xf0000)>>16));
# else
b_addr += (4+(inst&0x30)+((inst&0xf000)>>12));
# endif
}
#endif /* XCHAL_HAVE_DENSITY */
inst_type_mask = XTHAL_24_BIT_INST|XTHAL_BRANCH_INST|XTHAL_DEST_REL_INST;
if ((inst_type&inst_type_mask)==inst_type_mask) {
#if XCHAL_HAVE_BE
if ((inst&0xf0000000)==0x70000000)
offset = ((int)(inst<<16))>>24;
else if ((inst&0xf2000000)==0x62000000)
offset = ((int)(inst<<16))>>24;
else
offset = ((int)(inst<<12))>>20;
#else
if ((inst&0xf)==0x7)
offset = ((int)(inst<<8))>>24;
else if ((inst&0x2f)==0x26)
offset = ((int)(inst<<8))>>24;
else
offset = ((int)(inst<<8))>>20;
#endif
b_addr += 4 + offset;
}
inst_type_mask = XTHAL_24_BIT_INST|XTHAL_JUMP_INST|XTHAL_DEST_REL_INST;
if ((inst_type&inst_type_mask)==inst_type_mask) {
#if XCHAL_HAVE_BE
if ((inst&0xfc000000)==0x60000000)
offset = ((int)(inst<<6))>>14;
else
{
b_addr &= 0xfffffffc;
offset = ((int)(inst<<6))>>12;
}
#else
if ((inst&0x3f)==0x6)
offset = ((int)(inst<<8))>>14;
else
{
b_addr &= 0xfffffffc;
offset = ((int)(inst<<8))>>12;
}
#endif
b_addr += 4 + offset;
}
return b_addr;
}
// return pc of next instruction for a given state
unsigned int xthal_get_npc(XTHAL_STATE *user_state)
{
unsigned inst_type;
unsigned npc;
inst_type = xthal_inst_type((void *)user_state->pc);
if (inst_type & XTHAL_24_BIT_INST)
npc = user_state->pc + 3;
else
npc = user_state->pc + 2;
if (inst_type & XTHAL_RFW_INST) {
/* Can not debug level 1 interrupts */
// xt_panic();
} else if (inst_type & XTHAL_RFUE_INST) {
/* Can not debug level 1 interrupts */
// xt_panic();
} else if (inst_type & XTHAL_RFI_INST) {
/* Can not debug level 1 interrupts */
// xt_panic();
} else if (inst_type & XTHAL_RFE_INST) {
/* Can not debug level 1 interrupts */
// xt_panic();
} else if (inst_type & XTHAL_RET_INST) {
npc = (user_state->pc&0xc0000000)+(user_state->ar[0]&0x3fffffff);
} else if (inst_type & XTHAL_BREAK_INST) {
/* Can not debug break */
// xt_panic();
} else if (inst_type & XTHAL_SYSCALL_INST) {
/* Can not debug exceptions */
// xt_panic();
} else if (inst_type & XTHAL_LOOP_END) {
// xt_panic();
} else if (inst_type & XTHAL_JUMP_INST) {
if (inst_type & XTHAL_DEST_REG_INST) {
return user_state->ar[inst_type>>28];
} else if (inst_type & XTHAL_DEST_REL_INST) {
return xthal_branch_addr((void *)user_state->pc);
}
} else if (inst_type & XTHAL_BRANCH_INST) {
int branch_taken = 0;
unsigned short inst;
unsigned char op0, t, s, r, m, n;
memcpy(&inst, (void *)user_state->pc, 2);
#if XCHAL_HAVE_BE
op0 = (inst&0xf000)>>12;
t = (inst&0x0f00)>>8;
s = (inst&0x00f0)>>4;
r = (inst&0x000f);
m = t&3;
n = t>>2;
#else
op0 = (inst&0x000f);
t = (inst&0x00f0)>>4;
s = (inst&0x0f00)>>8;
r = (inst&0xf000)>>12;
m = t>>2;
n = t&3;
#endif
if (inst_type &XTHAL_16_BIT_INST) {
#if XCHAL_HAVE_BE
if (inst&0x400) /* BNEZ.N */
branch_taken = (user_state->ar[(inst>>4)&0xf]!=0);
else /* BEQZ.N */
branch_taken = (user_state->ar[(inst>>4)&0xf]==0);
#else
if (inst&0x40) /* BNEZ.N */
branch_taken = (user_state->ar[(inst>>8)&0xf]!=0);
else /* BEQZ.N */
branch_taken = (user_state->ar[(inst>>8)&0xf]==0);
#endif
}
if (op0==0x6) {
if (n==1) {
if (m==0) { /* BEQZ */
branch_taken = (user_state->ar[s]==0);
} else if (m==1) { /* BNEZ */
branch_taken = (user_state->ar[s]!=0);
} else if (m==2) { /* BLTZ */
branch_taken = (((int)user_state->ar[s])<0);
} else if (m==3) { /* BGEZ */
branch_taken = (((int)user_state->ar[s])>=0);
}
} else if (n==2) {
int b4const[16] =
{ -1, 1, 2, 3, 4, 5, 6, 7,
8, 10, 12, 16, 32, 62, 128, 256 };
if (m==0) { /* BEQI */
branch_taken = (user_state->ar[s]==b4const[r]);
} else if (m==1) { /* BNEI */
branch_taken = (user_state->ar[s]!=b4const[r]);
} else if (m==2) { /* BLTI */
branch_taken = (((int)user_state->ar[s])<b4const[r]);
} else if (m==3) { /* BGEI */
branch_taken = (((int)user_state->ar[s])>=b4const[r]);
}
} else if (n==3) {
int b4constu[16] =
{ 32768, 65536, 2, 3, 4, 5, 6, 7,
8, 10, 12, 16, 32, 62, 128, 256 };
if (m==2) { /* BLTUI */
branch_taken = (user_state->ar[s]<b4constu[r]);
} else if (m==3) { /* BGEUI */
branch_taken = (user_state->ar[s]>=b4constu[r]);
}
}
} else if (op0==0x7) {
if (r==0) { /* BNONE */
branch_taken = ((user_state->ar[s]&user_state->ar[t])==0);
} else if (r==1) { /* BEQ */
branch_taken = (user_state->ar[s]==user_state->ar[t]);
} else if (r==2) { /* BLT */
branch_taken = ((int)user_state->ar[s]<(int)user_state->ar[t]);
} else if (r==3) { /* BLTU */
branch_taken = (user_state->ar[s]<user_state->ar[t]);
} else if (r==4) { /* BALL */
branch_taken = (((~user_state->ar[s])&user_state->ar[t])==0);
} else if (r==5) { /* BBC */
#if XCHAL_HAVE_BE
branch_taken = ((user_state->ar[s]&(0x80000000>>user_state->ar[t]))==0);
} else if (r==6) { /* BBCI */
branch_taken = ((user_state->ar[s]&(0x80000000>>t))==0);
} else if (r==7) { /* BBCI */
branch_taken = ((user_state->ar[s]&(0x80000000>>(t+16)))==0);
#else
branch_taken = ((user_state->ar[s]&(1<<user_state->ar[t]))==0);
} else if (r==6) { /* BBCI */
branch_taken = ((user_state->ar[s]&(1<<t))==0);
} else if (r==7) { /* BBCI */
branch_taken = ((user_state->ar[s]&(1<<(t+16)))==0);
#endif
} else if (r==8) { /* BANY */
branch_taken = ((user_state->ar[s]&user_state->ar[t])!=0);
} else if (r==9) { /* BNE */
branch_taken = (user_state->ar[s]!=user_state->ar[t]);
} else if (r==10) { /* BGE */
branch_taken = ((int)user_state->ar[s]>=(int)user_state->ar[t]);
} else if (r==11) { /* BGEU */
branch_taken = (user_state->ar[s]>=user_state->ar[t]);
} else if (r==12) { /* BNALL */
branch_taken = (((~user_state->ar[s])&user_state->ar[t])!=0);
} else if (r==13) { /* BBS */
#if XCHAL_HAVE_BE
branch_taken = ((user_state->ar[s]&(0x80000000>>user_state->ar[t]))!=0);
} else if (r==14) { /* BBSI */
branch_taken = ((user_state->ar[s]&(0x80000000>>t))!=0);
} else if (r==15) { /* BBSI */
branch_taken = ((user_state->ar[s]&(0x80000000>>(t+16)))!=0);
#else
branch_taken = ((user_state->ar[s]&(1<<user_state->ar[t]))!=0);
} else if (r==14) { /* BBSI */
branch_taken = ((user_state->ar[s]&(1<<t))!=0);
} else if (r==15) { /* BBSI */
branch_taken = ((user_state->ar[s]&(1<<(t+16)))!=0);
#endif
}
}
if (branch_taken) {
if (inst_type & XTHAL_DEST_REG_INST) {
return user_state->ar[inst_type>>24];
} else if (inst_type & XTHAL_DEST_REL_INST) {
return xthal_branch_addr((void *)user_state->pc);
}
}
#if XCHAL_HAVE_LOOPS
else if (user_state->lcount && (npc==user_state->lend))
return user_state->lbeg;
#endif
}
return npc;
}
#endif /* INCLUDE_DEPRECATED_HAL_DEBUG_CODE */