Comments on single-stepping in the X-Core

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

Comments on single-stepping in the X-Core

Post by pnathan »

Hi,

I am a part-time graduate student studying embedded multicore debugging. My project is to build a "reversible" debugger in software (not using JTAG), where I can load the trace of the system into a screen and walk through the states of the system.

I selected the XCore because it seemed to be the best-documented multicore processor that was available for a student.

I've been working on trying to get single-step working - the *preliminary* work for a trace, for nearly 1 year now (more or less off and on). This is brutally technical to get right and it still doesn't work correctly. I really wish that there was an x86-esque status-bit to set. Let me explain why.

Rough sketch of x86 debug algorithm:

1. Disable interrupts
2. Assign address of debug handler to the correct vector.
3. Set the CPU correct status bit to 1.
4. Enable interrupts.

XMOS:
Assign debug handler vector.
DCALL into the debug handler.
Configure the interconnect to begin throwing the debug interrupt on:
Called PC + (2 or 4). Here you have to request the called PC from the interconnect.
Specify the return address in the interconnect.

Note that XCC does not appear to have a compiler mechanism to generate correct code for a debug interrupt, which has forced me to write a Perl script to scrub some stack manipulation instructions.

Problems:
1. You have to get/set the PC you are coming from and going to, which leads to...
2. You have to parse out if the instruction is 2 bytes or 4 bytes, and
3. You have to manually parse the instruction in order to do branches correctly. Preliminary results suggest RET doesn't correctly read the LR when in debug mode, so I expect I will have to simulate RET/LR, much as I have done with branching.

These are not insurmountable problems. They can be solved with enough code and careful tabulating of which instructions require which actions.

However, I strongly feel that with some work on the hardware end, these problems would largely go away. In particular, calculating in software the next-PC address (whether normal, branching, or returning) is slightly ridiculous, since it could well be saved into a hardware register on interrupt.

Thank you for your time.


User avatar
kris
Experienced Member
Posts: 84
Joined: Mon Jan 18, 2010 2:52 pm

Post by kris »

Hi pnathan,

The IBREAK_CTRL register can be configured to break when the current pc
either equals or doesnt equal the value in the IBREAK_ADDR register. Therefore, to
single step, you only need to write the value of the current pc into the ADDR register,
set the condition to 'not equal', then continue. As soon as the pc changes, the ibreak will then
trigger and the core will re-enter debug mode. That saves you the trouble of working out
where the next pc is...

Hope this helps,
Cheers,
Kris.
pnathan
Junior Member
Posts: 6
Joined: Tue Apr 06, 2010 4:27 pm

Post by pnathan »

Hi Kris,

Thank you for your response. It certainly sounds as if I'm doing it the hard way around.

I'd like to describe the technique I'm using - if you have better suggestions, I would love to hear them.
Currently this is only set up for 1 thread on 1 core.

To begin breaking, I trigger DCALL and enable the breakpoints -

Code: Select all

#define IBREAK_ENABLE_REG 0x400B
#define IBREAK_PC_REG 0x300B

//JTAG register
#define DBG_PC      0x11

//This converts the JTAG register to the correct token.
#define REG_TO_ID(a) ((a << 8) | 0x0B)

#define PC_RID REG_TO_ID(DBG_PC)

/************************/
//Down in the debug handler function, I use these constants & instructions. Note that I've heavily //redacted my routine.


//To get the PC, I do this:
asm("ldc r0, %0" : : "i"(PC_RID));



//enable the register
setps(IBREAK_ENABLE_REG, 0x10003);

//define the PC to break when not equal to
setps(IBREAK_PC_REG, pc - (size_of_word)); 
//note that if I do pc, then it goes to PC, then breaks *on the next instruction* in the simulator.

//and write back the PC to return to
asm("ldc r0, %0" : : "i"(PC_RID));
//write it back
asm("set ps[r0], r1");