proposed extension for jtag debugging

Bart Veer bartv@ecoscentric.com
Mon Jul 7 21:17:00 GMT 2008


These days gdb can interact with many hardware debug solutions based
on jtag or BDM. Most solutions involve the remote protocol and a gdb
server of some sort, including: OpenOCD, m68k-elf-sprite,
nios2-gdb-server, and Abatron BDI units. There are exceptions, for
example http://sourceforge.net/projects/bdm/ which adds a
"target bdm" instead. Some solutions are open source, others are
proprietary.

Unfortunately these solutions typically do not provide any console
output, the equivalent of 'O' packets. Instead a variety of
workarounds are used. Often the output is sent out of a uart, but that
only works when (a) the processor has at least one uart and that uart
is accessible on the target hardware, and (b) the application does not
need all available uarts. Some processors come with special jtag
uarts, but those cause portability issues on both host and target. The
occasional printf() can be an invaluable debug tool, and it would be
useful to have a reasonably portable way of achieving console output
when debugging over jtag or BDM. Recently I have been doing some
experimenting along these lines.

Now, in addition to 'O' packets gdb supports console I/O via the
remote file I/O extension - as well as file I/O if desired. Hence one
approach is to tweak remote-fileio.c so that it can be invoked even
when the remote gdb server is not sending 'F' packets. The target-side
code looks something like this:

----------------------------------------------------------------------------
static int          _gdb_fileio_disabled   = 1;
static char         _gdb_fileio_pkt[GDB_FILEIO_MAX_PKTLEN];

static int
_gdb_fileio_call(void)
{
    if (_gdb_fileio_disabled) {
        return 0;
    }
    // On M68K the reported PC is immediately after the halt instruction.
    __asm__ volatile (  " halt\n"
                        " .globl  _gdb_hwdebug_breakpoint\n"
                        " .type   _gdb_hwdebug_breakpoint, function\n"
                        "_gdb_hwdebug_breakpoint:\n"
                        : : : "memory"
    );
    return 1;
}
----------------------------------------------------------------------------

I/O code will fill in _gdb_fileio_pkt[] with the contents of an 'F'
packet and then invoke _gdb_fileio_call(). This checks whether or not
file I/O is currently enabled. The flag _gdb_fileio_disabled is
cleared only when gdb is connected, allowing a single application to
work either stand-alone or inside a debug session. Next the code
triggers a breakpoint. The exact details of this are obviously
architecture-specific. Embedding the breakpoint instruction directly
in the code means that it will function even when debugging an
application programmed into flash, with no need to use up one of the
few available hardware breakpoints.

The corresponding gdb code lives in a new module hwdebug-fileio.c. The
code is disabled by default, things only start happening following a
"set hwdebug on" command. Amongst other things that pushes a new set
of target_ops, the most interesting of which is hwdebug_wait():

----------------------------------------------------------------------------
static CORE_ADDR                target__gdb_hwdebug_breakpoint;
static CORE_ADDR                target__gdb_fileio_pkt;
static char                     gdb_fileio_pkt[GDB_FILEIO_MAX_PKTLEN + 1];

...

static ptid_t
hwdebug_wait(ptid_t ptid, struct target_waitstatus* status)
{
    ptid_t              result;
    struct target_ops*  orig_ops    = find_target_beneath (&hwdebug_ops);
    CORE_ADDR           pc;

    while (1 ) {
        result = (*orig_ops->to_wait) (ptid, status);
        pc = read_pc();
        if (pc != target__gdb_hwdebug_breakpoint) {
            break;
        }
        gdb_fileio_pkt[0] = 'F';
        target_read_memory(target__gdb_fileio_pkt, &(gdb_fileio_pkt[1]),
                           GDB_FILEIO_MAX_PKTLEN);
        remote_fileio_request(gdb_fileio_pkt, &hwdebug_putpkt);

        // As per wait_for_inferior()
        overlay_cache_invalid   = 1;
        registers_changed ();

        // Automatically resume the target.
        hwdebug_resume(ptid, hwdebug_resume__last_step,
	               hwdebug_resume__last_sig);
    }

    // The target has halted for reasons other than h/w debug file I/O,
    // e.g. an ordinary breakpoint. Return to higher-level gdb code.
    return result;
}
----------------------------------------------------------------------------

So basically this code chains to the underlying wait() function, e.g.
remote_wait(). When the target halts the PC is compared with
&_gdb_hwdebug_breakpoint. If there is a match then the 'F' packet is
fetched from the target and processed via remote_fileio_request(), and
then the target automatically resumes. Otherwise the event is passed
back up to higher-level code.

For this to work remote-fileio.c needs some tweaking so that it is
independent from remote.c - not all hardware debug solutions involve
the remote protocol and a gdb server. Calls to remote_read_bytes()
need to be replaced with target_read_memory(), and similar for writes
to target memory. The response to the file I/O request needs to go via
a putpkt() variant supplied as argument to remote_fileio_request(),
not the standard remote.c putpkt(). In addition it may be necessary to
have a new target ops stratum, e.g. process_override_stratum, sitting
between process_stratum and thread_stratum.

Obviously a full implementation will be somewhat more complicated than
the above. For example it will be necessary to cope with ctrl-C.
Generating the 'F' packet on the target is not necessarily the most
efficient approach. A binary structure on the target plus code in
hwdebug-fileio.c to generate the 'F' packet based on that structure
would save some target-side code and data, possibly significant on the
smaller targets. On the other hand it would add a lot of complexity to
the host-side code and make it more difficult to extend the protocol
in future - although I believe that has not happened since the
functionality was added in 2003.

There is no guarantee that the approach described here will work on
all hardware, since some gdb servers may get rather upset if the
target hits a breakpoint instruction that was not set by gdb. However
I think it should work with most systems.

At this stage I would like to invite some comments:

1) is there any interest in adding functionality along these lines,
   i.e. mostly portable console and host file I/O for jtag and BDM
   debug solutions, to mainstream gdb?

2) is the basic approach of reusing remote-fileio.c the right one?

Bart

--
Bart Veer                                   eCos Configuration Architect
eCosCentric Limited    The eCos experts      http://www.ecoscentric.com/
Barnwell House, Barnwell Drive, Cambridge, UK.      Tel: +44 1223 245571
Registered in England and Wales: Reg No 4422071.



More information about the Gdb mailing list