Getting CPU state out when in debug mode

Technical questions regarding the XTC tools and programming with XMOS.
pnathan
Junior Member
Posts: 6
Joined: Tue Apr 06, 2010 4:27 pm

Getting CPU state out when in debug mode

Post by pnathan »

Hi,

I have been working on getting a software debugger up and running. I have the debug handler to operating. My tasks now are to (1)convince the JTAG to set the thread-under-inspection to single-step and (2) to read the CPU state, ie, the registers.

Working with henk over on the xlinkers forum, I have come up with the following code. However, it does not yield the correct registers.

Code: Select all

//sourced from pg 105 in xs1_en.pdf: 2009-10-19 release
//Each resource has a different type
#define RES_TYPE_FOR_THREAD 0x4
//This is for a given thread #
#define RES_FOR_THREAD(a) ((a << 24) | RES_TYPE_FOR_THREAD)

//C function called by the debug handler.
void load_cpu_state()
{  
    int i,j,k;
    int cur_thread = 0;
    int threadID;
    int temp;
    
   //what is the ID of our thread? - we're going to focus on thread 0 right now.
    threadID = RES_FOR_THREAD(cur_thread);

   //initialize our constants
    //r0 is thread ID constant
    asm("mov r0, %0" :: "r"(threadID) : "r0");
    //r1 is address in the JTAG to set thread ID
    asm("ldc r1, %0" :: "i"(RES_RID) : "r1");

     //Set the JTAG to know what thread info we need.
    //Set the JTAG[Thread ID] = CurrentThreadConstant
    asm("set ps[r1], r0");    
    
    
    //Now we walk from r0 to r11.
    for( i = 0; i < 12; i++)
    {   
     temp = load_pushed_reg(i); //load_pushed_reg() goes out and grabs the registers.
     say_num(temp); //Writes it out on the serial line.	
     cpu_state.r[i] = temp;       
    }
    
} //Exits back to the debug handler.

//Gets regnum
int load_pushed_reg(int regnum)
{
    int ret;

    //Using the mechanic of 'load settings', then GETREG, then mov the reg into a variable and return the variable.
    //regnum is the register we want info about
    asm("mov r1, %0" :: "r"(regnum) : "r1");    // writes r1
    //r0 gets the DBG_T_REG number (0x14b)
    asm("ldc r0, %0" :: "i"(0x140B));
    //Set JTAG[Reg] = resource ID
    asm("set ps[r0], r1");       

    //Pull the register value back from the JTAG
    asm("dgetreg r2" ::: "r2");
    //Save the register value into ret.
    asm("mov %0, r2" : "=r"(ret));       

    return ret;
}


User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

When you use dgetreg, you need to specify both the register number and the thread number that you want to access. Are you doing this? I can only see the register number being set not the thread number. This is the assembler code sequence I use for accessing registers on other threads (i.e. not T0) during debug:

Code: Select all

ldc r1, 2 // read a register on thread 2
ldc r0, PS_DBG_T_NUM
set ps[r0],r1

ldc r1, DBG_T_REG_PC_NUM // read the PC
ldc r0, PS_DBG_T_REG
set ps[r0],r1

dgetreg r2 // loads r2 with the T2's PC

pnathan
Junior Member
Posts: 6
Joined: Tue Apr 06, 2010 4:27 pm

Post by pnathan »

Hi Woody,

In particular answer to your question, I'm using load_cpu_state() to set the thread ID, and load_pushed_reg() to get the data from the registers.

I am focused on working with thread 0 on the different cores. Using your constants seems to cause the CPU to hang on the first set ps instruction.

Looking over henk's information as compared to yours in the link herehttp://www.xmoslinkers.org/forum/viewto ... ?f=6&t=730, it's different; there are some shifts to get the constants into a resource ID mode.
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

I've not been through the whole of your thread with Henk, but you do not put a resourceID for the thread (e.g. 0x00000204 for thread 2), you do just use the thread number (0-7).

There is a bug in the code I gave you (sorry). You need to ensure there is a nop between the final setps and the dgetreg. This may help.

dgetreg works on all threads.

Code: Select all

ldc r1, 2 // read a register on thread 2
ldc r0, PS_DBG_T_NUM
set ps[r0],r1

ldc r1, DBG_T_REG_PC_NUM // read the PC
ldc r0, PS_DBG_T_REG
set ps[r0],r1

nop // required to ensure setps has completed before dgetreg

dgetreg r2 // loads r2 with the T2's PC
pnathan
Junior Member
Posts: 6
Joined: Tue Apr 06, 2010 4:27 pm

Post by pnathan »

That looks like it's approximately right - there is some bad output, but some good (as opposed to all bad :D), so it's progress.

Question: the nop for the delay - I don't see that documented in SET or DGETREG. How did you find out about it?

edit:

Moving further: the code below gives this output:
0 : 104B
1 : 1
2 : 2
3 : 0
4 : 6
5 : 7
6 : 8
7 : 9
8 : A
9 : B
A : C
B : 0


Problem: each r was been assigned to i+2 immediately before the DCALL. Notice that registers 4-A have valid values.

What this appears to me is that the JTAG's registers are getting partially overwritten.

Thoughts?

Code: Select all

//Gets regnum
int load_pushed_reg(int regnum)
{
    int ret = -1;
    //regnum is the register we want info about
    asm("mov r1, %0" :: "r"(regnum) : "r1"); 	// writes r1
    //r0 gets the DBG_T_REG number (0x14b)
    asm("ldc r0, %0" :: "i"(0x140B));
    
    //Set JTAG[Reg] = resource ID
    asm("set ps[r0], r1");      
    asm("nop");
    //Pull the register value back from the JTAG
    asm("dgetreg r2" ::: "r2");

    //Save the register value into temp.
    asm("mov %0, r2" : "=r"(ret));       
    
    put_num(regnum);
    put_s(" : ");
    say_num(ret);
    

    return ret;
}
User avatar
Woody
XCore Addict
Posts: 165
Joined: Wed Feb 10, 2010 2:32 pm

Post by Woody »

In debug mode T0 is still active and executing code. I would expect T0r0 to have the value of 0x104b, because that is what you've loaded it with before the DGETREG instruction. This suggests that DGETREG is working.

I would expect the same to be true of the other T0 read registers: i.e. the value reported is the value that these registers have when DGETREG instruction is executed, not when debug mode is entered. If you want to retain the values of the registers when debug mode is entered, your debug monitor needs to save them at the start of the monitor, then use those values when accessing T0 rather than performing a DGETREG.

T1-7 are not active during debug mode so you can read their register values without the need to save their values to memory first.
pnathan
Junior Member
Posts: 6
Joined: Tue Apr 06, 2010 4:27 pm

Post by pnathan »

Hmmm. That makes a lot of sense. :ugeek:

But I'm wondering how you found this out. I didn't read it in the documentation and I've been over the XS-1 architecture book, the XSystem and XSystem-L references quite a few times. It'd be handy to know this. :)