When the run command is used to start execution
with GDB it needs to establish the executable on the inferior, and
then start execution. This is done using the
to_create_inferior
and
to_resume
functions of the target respectively.
Once execution has started, GDB waits until the target
to_wait
function returns control.
In addition the target provides operations to stop execution.
or1k_resume
. This is the function which
causes the target program to run. It is called in response to
the run, step,
stepi, next and
nexti instructions.
The behavior of this function is far simpler than its
counterpart in GDB 5.3, which required complex logic to
re-execute instructions after a breakpoint or
watchpoint. GDB 6.8 will sort out all the issues of
re-execution after a breakpoint or watchpoint has been
encountered (see or1k_wait
below for more
on this).
The function clears the Debug Reason Register, clears any watchpoint status bits in Debug Mode Register 2 and then commits the debug registers.
If the caller has requested single stepping, this is set using Debug Mode Register 1, otherwise this is cleared.
Finally the target can be marked as executing (the first time
or1k_resume
is called it will not be marked
as executing), the debug registers written out, and the processor
unstalled.
or1k_wait
. This function waits for the
target to stall, and analyzes the cause. Information about why
the target stalled is returned to the caller via the
status
argument. The function returns the
process/thread ID of the process which stalled, although for
the OpenRISC 1000 this will always be the same value.
While waiting for the target to stall (using
or1k_jtag_wait
), a signal handler is
installed, so the user can interrupt execution with ctrl-C.
After the wait returns, all register and frame caches are
invalid, These are cleared by calling
registers_changed
(which in turn clears
the frame caches).
When the processor stalls, the Debug Reason Register (a SPR)
shows the reason for the stall. This will be due to any
exception set in the Debug Stop Register (currently only trap),
due to a single step, due to a reset (the debugger stalls the
processor on reset) or (when the target is the architectural
simulator, Or1ksim) due to an exit
l.nop 1
being executed.
In all cases the previous program counter SPR points to the instruction just executed and the next program counter SPR to the instruction about to be executed. For watchpoints and breakpoints, which generate a trap however the instruction at the previous program counter will not have completed execution. As a result, when the program resumes, this instruction should be re-executed without the breakpoint/watchpoint enabled.
GDB understands this. It is sufficient to set the program
counter to the previous program counter. GDB will realize
that the instruction corresponds to a breakpoint/watchpoint
that has just been encountered, lift the breakpoint, single
step past the instruction and reimpose the breakpoint. This is
achieved by a call to write_pc
with the
previous program counter value.
The OpenRISC 1000 imposes a slight problem here. The standard GDB approach works fine, except if the breakpoint was in the delay slot of a branch or jump instruction. In this case the re-execution must be not just of the previous instruction, but the one before that (restoring the link register as well if it was a jump-and-link instruction). Furthermore this must only be in the case where the branch was truly the preceding instruction, rather than the delay slot having been the target of a different branch instruction.
In the absence of a "previous previous" program counter, this restart cannot be correct under all circumstances. For the time being, breakpoints on delay slots are not expected to work. However it is highly unlikely a source level debugger would ever place a breakpoint in a delay slot.
A more complete solution for the future would use the struct gdbarch
adjust_breakpoint_address
to move any
breakpoint requested for a delay slot, to insist the breakpoint
is placed on the preceding jump or branch. This would work for
all but the most unusual code, which used a delay slot as a
branch target.
Having sorted out the program counter readjustment, any single
step is marked as though it were a trap. Single step does not
set the trap exception, nor does it need re-executing, but by
setting the flag here, the exception will be correctly mapped
to the TARGET_SIGNAL_TRAP
for return to
GDB
The response is marked as a a stopped processor
(TARGET_WAITKIND_STOPPED
). All
exceptions are mapped to their corresponding GDB signals. If
no exception has been raised, then the signal is set to the
default, unless the instruction just executed was
l.nop 1
, which is used by the
architectural simulator to indicate termination. In this case
the response is marked as
TARGET_WAITKIND_EXITED
, and the associate
value set to the exit return code.
The debug reason register (which is sticky) can now be cleared and the process/thread ID returned.
or1k_stop
. This stops the processor
executing. To achieve this cleanly, the processor is stalled,
single step mode is set and the processor unstalled, so
execution will have stopped at the end of an instruction.
or1k_kill
. This is a more dramatic
termination, when or1k_stop
has failed to
give satisfaction. Communication with the target is assumed to
have broken down, so the target is then mourned, which will
close the connection.
or1k_create_inferior
. This sets up a
program to run on the target, but does not actually start it
running. It is called in response to the GDB
run command and is passed any arguments to
that command. However the OpenRISC 1000 JTAG protocol has no
way to send arguments to the target, so these are ignored.
Debugging is much easier if a local copy of the executable symbol table has been loaded with the file command. This is checked for and a warning issued. However if it is not present, it is perfectly acceptable to debug code on the OpenRISC 1000 target without symbol data.
All static data structures (breakpoint lists etc) are then
cleared within GDB by calling
init_wait_for_inferior
.
or1k_mourn_inferior
. This is the
counterpart to or1k_create_inferior
,
called after execution has completed. It tidies up by calling
the generic function
generic_mourn_inferior
. If the target is
still shown as having execution, it is marked as exited, which
will cause the selection of a new current target.