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

4.6.1.  Setting the Next Program Counter SPR

The OpenRISC 1000 instruction set architecture specifies two SPRs describing the program counter. The Previous Program Counter (PPC) represents the address of the instruction just completed. The Next Program Counter (NPC) represents the address of the instruction about to be executed.

However the OpenRISC 1000 is a pipelined processor with a 4/5 stage pipeline, so at any one time up to 4 instructions can be at some stage of execution. The pipeline stages are:

The PPC represents the address of the instruction that has just completed the write back stage of the pipeline. The NPC represents the address of the instruction which is the next to reach the write back stage of the pipeline (which may be at an earlier phase than write back, if the pipeline is not currently full).

The problem comes if the NPC is written while the processor is stalled. This must cause a flush of the pipeline, so until the processor is unstalled there is no instruction anywhere in the pipeline waiting to be executed. Thus a subsequent read of the NPC will return zero, not the value just written.

This behavior can be seen by following a VCD trace through a number of debug actions. This uses the "Hello World" example from Section 4.5.1. The program is loaded and run to a breakpoint set at the start of main (address 0x12f4).

(gdb) target remote :51000
(gdb) load hello
Loading section .text, size 0x1350 lma 0x0
Loading section .rodata, size 0x1f lma 0x1350
Start address 0x100, load size 4975
Transfer rate: 82 bytes/sec, 236 bytes/write.
(gdb) break main
Breakpoint 1 at 0x12f4: file hello.c, line 26.
(gdb) continue
Continuing.

Breakpoint 1, main () at hello.c:26
26        simputs ("Hello World!\n");
	  (gdb) 	

At this point (111,173.85μs), the wave trace in Figure 4.9 shows the processor stalling. The program counter for the write back stage is the address of break point (0x12f4) and the instruction associated with the write back phase is the l.trap instruction (0x21000001) used to generate the trap.

VCD trace of the OpenRISC 1000 pipeline following a l.trap stall.

Figure 4.9.  VCD trace of the OpenRISC 1000 pipeline following a l.trap stall.


The succeeding instructions can be seen part processed in the pipeline. Disassembling around the breakpoint, the assembly code is:

(gdb) disassemble 0x12f0 0x1300
Dump of assembler code from 0x12f0 to 0x1300:
0x000012f0 <main+12>:   l.sw     0(r1),r9
0x000012f4 <main+16>:   l.movhi  r3,0x0
0x000012f8 <main+20>:   l.ori    r3,r3,0x1350
0x000012fc <main+24>:   l.jal    <simputs>
End of assembler dump.
(gdb)
	  

The instruction at the breakpoint (0x12f4) is not l.movhi as shown in the disassembly, but has been replaced by the l.trap to cause the breakpoint. The next instruction, at location 0x12f8 is l.ori r3,r3,0x1350 (0xa8631350). This instruction is between instruction decode and execution stages. The address is shown in both stages and the instruction itself can be seen in the instruction decode stage.

On hitting a breakpoint, the first action of GDB is to set the program counter back by one instruction. This is because the instruction that was replaced by l.trap for the breakpoint must be put back so it can be executed before resuming any further execution. This involves writing the NPC, changing it from its value of 0x12f8 (the l.ori instruction) back to 0x12f4.

This behavior can be seen in Figure 4.10 at time 104,890.85μs. The write back program counter is left unchanged (0x12f4), since the instruction has already been executed. However the program counters for instruction decode and execute are set to zero. The program counter for instruction fetch is set to the new value, 0x12f4. The instruction registers for all stages have nonsense values in them (there is no OpenRISC 1000 instruction beginning with 0x14).

VCD trace of the OpenRISC 1000 pipeline following a write setting NPC to 0x12f4.

Figure 4.10.  VCD trace of the OpenRISC 1000 pipeline following a write setting NPC to 0x12f4.


The Next Program Counter (NPC) is used in GDB as the value of the Program Counter ($pc). This can be changed using the set command:

(gdb) set $pc=0x100
(gdb)
	  

The value appears as the new address for the instruction decode stage of the pipeline. This is shown in Figure 4.11 at time 110,264.85μs. Addresses for all other pipeline stages are unchanged, and the instruction values for these stages are still meaningless.

VCD trace of the OpenRISC 1000 pipeline following a second write setting NPC to 0x100.

Figure 4.11.  VCD trace of the OpenRISC 1000 pipeline following a second write setting NPC to 0x100.


Once the pipeline is unstalled, it will refill. This can be achieved by continuing execution:

(gdb) continue
Continuing.

Breakpoint 1, main () at hello.c:26
26        simputs ("Hello World!\n");
(gdb) 
	  

Execution will restart from the new value of NPC (0x100, the reset vector). A disassembly shows the instructions at that address:

(gdb) disassemble 0x100 0x110
Dump of assembler code from 0x100 to 0x110:
0x00000100 <_start+0>:  l.addi   r1,r0,32512
0x00000104 <_start+4>:  l.addi   r2,r1,0
0x00000108 <_start+8>:  l.mfspr  r3,r0,0x11
0x0000010c <_start+12>: l.ori    r3,r3,0x2
End of assembler dump.
(gdb)
	  

The VCD trace in Figure 4.12 shows the processor unstalling at time 111,173.85μs. In consecutive cycles the pipeline fills with the instructions from locations 0x100 (0x9c207f00, l.addi r1,r0,0x7f00, 0x104 (0x9c410000, l.addi r2,r1,0), 0x108 (0xb4600011, l.mfspr r3,r0,0x11) and 0x10c (0xa8630002, l.ori r3,r3,0x2).

VCD trace of the OpenRISC 1000 pipeline refill when the CPU is unstalled.

Figure 4.12.  VCD trace of the OpenRISC 1000 pipeline refill when the CPU is unstalled.


This complexity of behavior is not generally what is wanted by a debugger. GDB will regularly write the NPC to a new value, but expects that subsequent reads will return the value just written. It is therefore essential that notwithstanding any other arrangements the NPC must be cached while the processor is stalled.

GDB from time to time may write the NPC to its current value. The intention is that this should have no effect, yet if the NPC SPR is written the pipeline will be flushed. This can be particularly catastrophic if the flush causes a delayed branch to be lost.

Thus the interface must ensure that any request to write the value of the NPC does nothing if the value is the same as the value already there.

Embecosm divider strip