Services - tools - models - for embedded software development
Embecosm divider strip
Prev  Next

4.6.3.  Hardware Single Step

This is a genuine bug in the interaction between Debug Unit and CPU. When hardware single step is used, the pipeline can become confused, leading to multiple executions of the same instruction, and eventually to the same instruction being executed forever.

Hardware single-step is always used in two places. First when restarting after a breakpoint to execute the instruction that was replaced by l.trap. GDB single steps the instruction, then replaces it with l.trap, so the breakpoint can be used again.

Secondly GDB uses single step for the stepi command.

A sequence of stepi commands illustrates the problem:

(gdb) si
0x00001224 in simputs (str=0x0) at utils.c:102
102     {
(gdb) si
0x00001228      102     {
(gdb) si
0x00001228      102     {
(gdb) si
0x0000122c      102     {
(gdb) si
0x00001230      102     {
(gdb) si
0x00001230      102     {
(gdb) si
0x00001230      102     {
(gdb) si
0x00001230      102     {
	  

The VCD trace in Figure 4.16 shows the pipeline failing to fill correctly after some of the single steps

VCD trace of the OpenRISC 1000 pipeline after multiple single steps.

Figure 4.16.  VCD trace of the OpenRISC 1000 pipeline after multiple single steps.


From around 140ms, the pipeline refill starts to go wrong. The ticks on the dbg_stall line are the individual single steps. By 180ms the pipeline is completely filled with the instruction at address 0x1230 (0xd7e21ffc, l.sw -4(r2),r3).

Unfortunately single stepping is sometimes used by GDB in other circumstances. For example a step (high level instruction step) or nextcommand> may use multiple steps rather than setting and running to a temporary breakpoint. Under these circumstances the GDB client will hang, because the target does not seem to reach its target.

There is a workaround, which is to use ctrl-C (twice) to break the connection and then reconnect. The OpenRISC 1000 is stalled at the time, so on reconnection will be at the same location. Using continue allows the pipeline to refill correctly.

(gdb) s
^C^CInterrupted while waiting for the program.
Give up (and stop debugging it)? (y or n) y
(gdb) target remote :51000
Remote debugging using :51000
0x00001230 in simputs (str=0x1350 "Hello World!\n") at utils.c:102
102     {
(gdb) c
Continuing.

Breakpoint 4, simputs (str=0x1350 "Hello World!\n") at utils.c:105
105       for( i = 0; str[i] != '\0' ; i++ ) {
(gdb) 
	  

Solving the Problem

The solution is to modify the handling of the RSP step (s) packet, so that two consecutive hardware single-step operations are never used. A second step is implemented using l.trap.

This is not completely trivial, since the case where the second step would have occurred on a branch delay slot must be handled. Fortunately under this circumstance the single step bug does not seem to occur, so it is sufficient to permit a second single step in the case where that step occurs on a delay slot.

The example code distributed with this application note does not have this solution implemented. It is left as an exercise for the reader.

Embecosm divider strip