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:
Instruction Fetch
Instruction Decode
Execute
Memory access (only for load/store instructions). Will stall the pipeline while the memory is accessed.
Write Back
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.
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).
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.
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
).
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.