eCos Product

eCos Net Distribution


RedBoot Product
 
RedBoot Net Distribution


Supported Hardware

Downloading and Installation

Documentation

FAQ

Keeping in Touch

Problems

Licensing

Anonymous CVS

Contributions and Third Party Projects

Red Hat eCos

eCos GDB stubs [TBD/in progress]


The eCos common HAL contains target stubs which know the protocol used by GDB. This means that GDB can debug an application on the target, using the stubs features to control execution and examine memory/registers. See [FIXME:ref] here for details on the protocol used by GDB.

Implementing stubs for a new HAL consists of two parts. The first is the architecture or variant part which is used by the common stub code to control the CPU, access register contents, and translate exception codes into UNIX signal numbers. The second part is required for each new platform, and is the implementation of a simple device driver used by the common stub to communicate with GDB via the platform's IO hardware.


Architecture Stub Support [TBD]

The various features required by the architecture HAL are described below. Take a look an existing implementation to put it all in context.

Startup Initialization

The common stubs need to be initialized before the application proper is started. This is done with a call to initialize_stub after the target has been fully initialized.

After this, the function hal_ctrlc_isr_init should be called to initialize the device driver for asynchronous breakpoints.

Both calls should be made conditional on the configuration. In the SH HAL this looks like this:

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        mov.l    $_initialize_stub,r1
        jsr      @r1
         nop
#endif
#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) \
    || defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
        mov.l    $_hal_ctrlc_isr_init,r1
        jsr      @r1
         nop
#endif

        mov.l    $_cyg_start,r1
        jsr      @r1
         nop

Register Definitions

The architecture stub header file must contain an enumeration of register names and macros defining number of registers and the register sizes. This is the definitions from the PPC HAL:

#define NUMREGS    71

#define REGSIZE( _x_ ) (((_x_) >= F0 && (_x_) <= F31) ? 8 : 4)

enum regnames {
    R0, R1, R2, R3, R4, R5, R6, R7, 
    R8, R9, R10, R11, R12, R13, R14, R15,
    R16, R17, R18, R19, R20, R21, R22, R23, 
    R24, R25, R26, R27, R28, R29, R30, R31, 
    F0, F1, F2, F3, F4, F5, F6, F7, 
    F8, F9, F10, F11, F12, F13, F14, F15, 
    F16, F17, F18, F19, F20, F21, F22, F23, 
    F24, F25, F26, F27, F28, F29, F30, F31, 
    PC, PS, CND, LR, CNT, XER, MQ
};

Global Data Access

If the architecture uses a "Global Pointer" register for accessing data, CYGARC_HAL_SAVE_GP and CYGARC_HAL_RESTORE_GP macros must be implemented. These allow switching between the GP data area used by the RAM startup application, and that used by the stub code in ROM.

Single-stepping

It is possible to implement single-stepping in one of two ways. The first (and simplest and most effective) is to use a CPU builtin single-stepping control. The SH and PPC HALs use this method.

The other method requires code to examine the instruction at the PC to determine which instruction will be executed next - a breakpoint is then placed on that instruction. ARM and MIPS HALs use this method.

Signal Translation

The function __computeSignal() must provide a translation from the HAL exception number to the standard UNIX signal number.

On some architectures it may be necessary to read some of the saved registers to determine what signal an exception should be translated to - in this case, use the get_register() function to acquire register values.


Platform Stub Support

There are two parts to the platform stub support. The first part is the simple communication features. The second part is the asynchronous breakpoints, which should be implemented last (since they are more useful for application development than for stub/eCos debugging).

GDB Communication [TBD]

FIXME:See calling interface

Asynchronous GDB breakpoints

 FIXME:
What happens is that the stubs enable serial receive interrupts, but
do not change the vector. When a serial interrupt happens, it causes
the cyg_hal_default_isr of the application to be run. This calls into
the stub (via the virtual vectors) which determines if the received
character was a 0x03. If so, a breakpoint is put at the PC where the
interrupt happened.

GDB allows to asynchronously break execution of a running application. This is done by sending a 0x03 character to the target. If the serial device has interrupts enabled the interrupt execution path will eventually pass through hal_default_isr (see the Note below) which will check for the 0x03 character and issue a breakpoint. This will cause the ROM monitor to be entered, and GDB resumes control of the target.

Note: if a serial device driver is enabled and is using the serial receive character interrupt vector, the character will never reach the asynchronous breakpoint handling code, and thus GDB will not be able to break the application execution. If possible (not available on all platforms), use different serial devices for debugging and serial communication.

Implementation details

The implementation consists of two parts:

  1. The serial driver initialization and ISR code:

    IRQ_ENABLE/IRQ_DISABLE
    The driver's comm table provides a control function. This function must respond to IRQ_ENABLE and IRQ_DISABLE commands, enabling respectively disabling interrupts on the device.
    Enabling the interrupts may also involve configuring interrupt levels on the CPU/interrupt controller.
    DBG_ISR
    This ISR will examine the device. If the device has received characters, check if any of these are ctrlc characters from GDB (using cyg_hal_is_break), and if so, sets the ctrlc flag, otherwise clears it.
    If the device was the cause of an interrupt, acknowledge it, and return CYG_ISR_HANDLED, otherwise return 0.
    Note that the ISR is not attached to any vector. Instead it will be called from the hal_ctrlc_isr function which in turn is called from default_isr_handler.

  2. The common code

    start
    The architecture initialization code will call hal_ctrlc_isr_init during startup to enable the serial interrupt. This should happen after the call to initialize_stub.
    hal_default_isr
    Will check if the vector matches the DBG_ISR_VECTOR reported by the comms vector control function, and if so call hal_ctrlc_isr.
    hal_ctrlc_isr
    This will call the DBG_ISR function provided by the device driver. If the ctrlc flag is set on return, cyg_hal_user_break is called to set a breakpoint at the interrupt return address (to be hit when returning from interrupt code to normal user code *).
    * caveat: If the breakpoint is set in some code which will never be reached again (e.g., in a thread which gets scheduled out and killed during interrupt_exit) the breakpoint may never get hit. But it should not happen so often that it is a problem.
    cyg_hal_user_break
    Will use the ROM/RAM calling interface to place a breakpoint at the specified address.
    If there is no calling interface, simply use BREAKPOINT which is a hardwired breakpoint. That becomes a dead end since GDB will not know how to continue from there.


Thread debugging [TBD]

Application will set DBG_SYSCALL in virtual vector table, allowing ROM
monitor to call into the application kernel context and retrieve
debugging information. This is all set up in hal_if_init().