Provide basic documentation on the ARM ETM and ETB trace commands.
Fix minor goofs in registration of the ETM commands; and whitespace
issues in the proof-of-concept oocd_trace code. (Plus include a
ref to Dominic's email saying that it's just proof-of-concept code.)
Note that I'm still not sure whether the ETM support works. But
documenting how it's expected to work should help sort out which
behaviors are bugs, which will help get bugs patched.
---
doc/openocd.texi | 191 +++++++++++++++++++++++++++++++++++++++++++---
src/target/etm.c | 8 +
src/target/oocd_trace.c | 109 +++++++++++++-------------
3 files changed, 242 insertions(+), 66 deletions(-)
Provide basic documentation on the ARM ETM and ETB trace commands.
Fix minor goofs in registration of the ETM commands; and whitespace
issues in the proof-of-concept oocd_trace code. (Plus include a
ref to Dominic's email saying that it's just proof-of-concept code.)
Note that I'm still not sure whether the ETM support works. But
documenting how it's expected to work should help sort out which
behaviors are bugs, which will help get bugs patched.
---
doc/openocd.texi | 191 +++++++++++++++++++++++++++++++++++++++++++---
src/target/etm.c | 8 +
src/target/oocd_trace.c | 109 +++++++++++++-------------
3 files changed, 242 insertions(+), 66 deletions(-)
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -1094,17 +1094,11 @@ Some ARM cores are equipped with trace s
examination of the instruction and data bus activity. Trace
activity is controlled through an ``Embedded Trace Module'' (ETM)
on one of the core's scan chains. The ETM emits voluminous data
-through a ``trace port''. The trace port is accessed in one
-of two ways. When its signals are pinned out from the chip,
-boards may provide a special high speed debugging connector;
-software support for this is not configured by default, use
-the ``--enable-oocd_trace'' option. Alternatively, trace data
-may be stored an on-chip SRAM which is packaged as an ``Embedded
-Trace Buffer'' (ETB). An ETB has its own TAP, usually right after
-its associated ARM core. OpenOCD supports the ETM, and your
-target configuration should set it up with the relevant trace
-port: ``etb'' for chips which use that, else the board-specific
-option will be either ``oocd_trace'' or ``dummy''.
+through a ``trace port''. (@xref{ARM Tracing}.)
+If you are using an external trace port,
+configure it in your board config file.
+If you are using an on-chip ``Embedded Trace Buffer'' (ETB),
+configure it in your target config file.
@example
etm config $_TARGETNAME 16 normal full etb
@@ -3586,6 +3580,181 @@ OpenOCD packages most such operations in
Some of those operations don't fit well in that framework, so they are
exposed here using architecture or implementation specific commands.
+...@anchor{arm Tracing}
+...@subsection ARM Tracing
+...@cindex ETM
+...@cindex ETB
+
+CPUs based on ARM cores may include standard tracing interfaces,
+based on an ``Embedded Trace Module'' (ETM) which sends voluminous
+address and data bus trace records to a ``Trace Port''.
+
+...@itemize
+...@item
+Development-oriented boards will sometimes provide a high speed
+trace connector for collecting that data, when the particular CPU
+supports such an interface.
+(The standard connector is a 38-pin Mictor, with both JTAG
+and trace port support.)
+Those trace connectors are supported by higher end JTAG adapters
+and some logic analyzer modules; frequently those modules can
+buffer several megabytes of trace data.
+Configuring an ETM coupled to such an external trace port belongs
+in the board-specific configuration file.
+...@item
+If the CPU doesn't provide an external interface, it probably
+has an ``Embedded Trace Buffer'' (ETB) on the chip, which is a
+dedicated SRAM. 4KBytes is one common ETB size.
+Configuring an ETM coupled only to an ETB belongs in the CPU-specific
+(target) configuration file, since it works the same on all boards.
+...@end itemize
+
+ETM support in OpenOCD doesn't seem to be widely used yet.
+
+...@quotation Issues
+ETM support may be buggy, and at least some @command{etm config}
+parameters should be detected by asking the ETM for them.
+It seems like a GDB hookup should be possible,
+as well as triggering trace on specific events
+(perhaps @emph{handling IRQ 23} or @emph{calls foo()}).
+There should be GUI tools to manipulate saved trace data and help
+analyse it in conjunction with the source code.
+It's unclear how much of a common interface is shared
+with the current XScale trace support, or should be
+shared with eventual Nexus-style trace module support.
+...@end quotation
+
+...@subsubsection ETM Configuration
+ETM setup is coupled with the trace port driver configuration.
+
+...@deffn {Config Command} {etm config} target width mode clocking driver
+Declares the ETM associated with @var{target}, and associates it
+with a given trace port @var{driver}. @xref{Trace Port Drivers}.
+
+Several of the parameters must reflect the trace port configuration.
+The @var{width} must be either 4, 8, or 16.
+The @var{mode} must be @option{normal}, @option{multiplexted},
+or @option{demultiplexted}.
+The @var{clocking} must be @option{half} or @option{full}.
+
+...@quotation Note
+You can see the ETM registers using the @command{reg} command, although
+not all of those possible registers are present in every ETM.
+...@end quotation
+...@end deffn
+
+...@deffn Command {etm info}
+Displays information about the current target's ETM.
+...@end deffn
+
+...@deffn Command {etm status}
+Displays status of the current target's ETM:
+is the ETM idle, or is it collecting data?
+Did trace data overflow?
+Was it triggered?
+...@end deffn
+
+...@deffn Command {etm tracemode} [type context_id_bits cycle_accurate branch_output]
+Displays what data that ETM will collect.
+If arguments are provided, first configures that data.
+When the configuration changes, tracing is stopped
+and any buffered trace data is invalidated.
+
+...@itemize
+...@item @var{type} ... one of
+...@option{none} (save nothing),
+...@option{data} (save data),
+...@option{address} (save addresses),
+...@option{all} (save data and addresses)
+...@item @var{context_id_bits} ... 0, 8, 16, or 32
+...@item @var{cycle_accurate} ... @option{enable} or @option{disable}
+...@item @var{branch_output} ... @option{enable} or @option{disable}
+...@end itemize
+...@end deffn
+
+...@deffn Command {etm trigger_percent} percent
+...@emph{buggy and effectively a NOP ... @var{percent} from 2..100}
+...@end deffn
+
+...@subsubsection ETM Trace Operation
+
+After setting up the ETM, you can use it to collect data.
+That data can be exported to files for later analysis.
+It can also be parsed with OpenOCD, for basic sanity checking.
+
+...@deffn Command {etm analyze}
+Reads trace data into memory, if it wasn't already present.
+Decodes and prints the data that was collected.
+...@end deffn
+
+...@deffn Command {etm dump} filename
+Stores the captured trace data in @file{filename}.
+...@end deffn
+
+...@deffn Command {etm image} filename [base_address] [type]
+Opens an image file.
+...@end deffn
+
+...@deffn Command {etm load} filename
+Loads captured trace data from @file{filename}.
+...@end deffn
+
+...@deffn Command {etm start}
+Starts trace data collection.
+...@end deffn
+
+...@deffn Command {etm stop}
+Stops trace data collection.
+...@end deffn
+
+...@anchor{trace Port Drivers}
+...@subsubsection Trace Port Drivers
+
+To use an ETM trace port it must be associated with a driver.
+
+...@deffn {Trace Port Driver} etb
+Use the @option{etb} driver if you are configuring an ETM
+to use on-chip ETB memory.
+...@deffn {Config Command} {etb config} target etb_tap
+Associates the ETM for @var{target} with the ETB at @var{etb_tap}.
+You can see the ETB registers using the @command{reg} command.
+...@end deffn
+...@end deffn
+
+...@deffn {Trace Port Driver} etm_dummy
+Use the @option{etm_dummy} driver if you are configuring an ETM that's
+not connected to anything (on-chip ETB or off-chip trace connector).
+...@emph{this driver lets OpenOCD talk to the ETM, but it does not expose
+any trace data collection.}
+...@deffn {Config Command} {etm_dummy config} target
+Associates the ETM for @var{target} with a dummy driver.
+...@end deffn
+...@end deffn
+
+...@deffn {Trace Port Driver} oocd_trace
+This driver isn't available unless OpenOCD was explicitly configured
+with the @option{--enable-oocd_trace} option. You probably don't want
+to configure it unless you've built the appropriate prototype hardware;
+it's @emph{proof-of-concept} software.
+
+Use the @option{oocd_trace} driver if you are configuring an ETM that's
+connected to an off-chip trace connector.
+
+...@deffn {Config Command} {oocd_trace config} target tty
+Associates the ETM for @var{target} with a trace driver which
+collects data through the serial port @var{tty}.
+...@end deffn
+
+...@deffn Command {oocd_trace resync}
+Re-synchronizes with the capture clock.
+...@end deffn
+
+...@deffn Command {oocd_trace status}
+Reports whether the capture clock is locked or not.
+...@end deffn
+...@end deffn
+
+
@subsection ARMv4 and ARMv5 Architecture
@cindex ARMv4 specific commands
@cindex ARMv5 specific commands
--- a/src/target/etm.c
+++ b/src/target/etm.c
@@ -1815,7 +1815,8 @@ int etm_register_commands(struct command
{
etm_cmd = register_command(cmd_ctx, NULL, "etm", NULL, COMMAND_ANY, "Embedded Trace Macrocell");
- register_command(cmd_ctx, etm_cmd, "config", handle_etm_config_command, COMMAND_CONFIG, "etm config <target> <port_width> <port_mode> <clocking> <capture_driver>");
+ register_command(cmd_ctx, etm_cmd, "config", handle_etm_config_command,
+ COMMAND_CONFIG, "etm config <target> <port_width> <port_mode> <clocking> <capture_driver>");
return ERROR_OK;
}
@@ -1823,12 +1824,13 @@ int etm_register_commands(struct command
int etm_register_user_commands(struct command_context_s *cmd_ctx)
{
register_command(cmd_ctx, etm_cmd, "tracemode", handle_etm_tracemode_command,
- COMMAND_EXEC, "configure trace mode <none|data|address|all> <context id bits> <cycle accurate> <branch output");
+ COMMAND_EXEC, "configure trace mode <none|data|address|all> "
+ "<context_id_bits> <cycle_accurate> <branch_output>");
register_command(cmd_ctx, etm_cmd, "info", handle_etm_info_command,
COMMAND_EXEC, "display info about the current target's ETM");
- register_command(cmd_ctx, etm_cmd, "trigger_percent <percent>", handle_etm_trigger_percent_command,
+ register_command(cmd_ctx, etm_cmd, "trigger_percent", handle_etm_trigger_percent_command,
COMMAND_EXEC, "amount (<percent>) of trace buffer to be filled after the trigger occured");
register_command(cmd_ctx, etm_cmd, "status", handle_etm_status_command,
COMMAND_EXEC, "display current target's ETM status");
--- a/src/target/oocd_trace.c
+++ b/src/target/oocd_trace.c
@@ -24,6 +24,11 @@
#include "oocd_trace.h"
#include "arm7_9_common.h"
+/*
+ * This is "proof of concept" code, for prototype hardware:
+ * https://lists.berlios.de/pipermail/openocd-development/2007-September/000336.html
+ */
+
static int oocd_trace_register_commands(struct command_context_s *cmd_ctx);
@@ -43,7 +48,7 @@ static int oocd_trace_read_reg(oocd_trac
}
LOG_DEBUG("reg #%i: 0x%8.8x\n", reg, *value);
-
+
return ERROR_OK;
}
@@ -87,7 +92,7 @@ static int oocd_trace_read_memory(oocd_t
else
bytes_to_read -= bytes_read;
}
-
+
return ERROR_OK;
}
@@ -96,7 +101,7 @@ static int oocd_trace_init(etm_context_t
u8 trash[256];
oocd_trace_t *oocd_trace = etm_ctx->capture_driver_priv;
size_t bytes_read;
-
+
oocd_trace->tty_fd = open(oocd_trace->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
if(oocd_trace->tty_fd < 0)
@@ -109,32 +114,32 @@ static int oocd_trace_init(etm_context_t
tcflush(oocd_trace->tty_fd, TCOFLUSH);
tcflush(oocd_trace->tty_fd, TCIFLUSH);
fcntl(oocd_trace->tty_fd, F_SETFL, fcntl(oocd_trace->tty_fd, F_GETFL) & ~O_NONBLOCK);
-
+
tcgetattr(oocd_trace->tty_fd, &oocd_trace->oldtio); /* save current port settings */
-
+
bzero(&oocd_trace->newtio, sizeof(oocd_trace->newtio));
oocd_trace->newtio.c_cflag = CS8 | CLOCAL | CREAD | B2500000;
-
+
oocd_trace->newtio.c_iflag = IGNPAR | IGNBRK | IXON | IXOFF;
oocd_trace->newtio.c_oflag = 0;
-
+
/* set input mode (non-canonical, no echo,...) */
oocd_trace->newtio.c_lflag = 0;
-
+
cfmakeraw(&oocd_trace->newtio);
oocd_trace->newtio.c_cc[VTIME] = 1; /* inter-character timer used */
oocd_trace->newtio.c_cc[VMIN] = 0; /* blocking read until 0 chars received */
-
+
tcflush(oocd_trace->tty_fd, TCIFLUSH);
tcsetattr(oocd_trace->tty_fd, TCSANOW, &oocd_trace->newtio);
-
+
/* occasionally one bogus character is left in the input buffer
* read up any leftover characters to ensure communication is in sync */
while ((bytes_read = read(oocd_trace->tty_fd, trash, sizeof(trash))) > 0)
{
LOG_DEBUG("%zi bytes read\n", bytes_read);
};
-
+
return ERROR_OK;
}
@@ -142,9 +147,9 @@ static trace_status_t oocd_trace_status(
{
oocd_trace_t *oocd_trace = etm_ctx->capture_driver_priv;
u32 status;
-
+
oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
-
+
/* if tracing is currently idle, return this information */
if (etm_ctx->capture_status == TRACE_IDLE)
{
@@ -155,18 +160,18 @@ static trace_status_t oocd_trace_status(
/* check Full bit to identify an overflow */
if (status & 0x4)
etm_ctx->capture_status |= TRACE_OVERFLOWED;
-
+
/* check Triggered bit to identify trigger condition */
if (status & 0x2)
etm_ctx->capture_status |= TRACE_TRIGGERED;
-
+
if (status & 0x1)
{
etm_ctx->capture_status &= ~TRACE_RUNNING;
etm_ctx->capture_status |= TRACE_COMPLETED;
}
}
-
+
return etm_ctx->capture_status;
}
@@ -191,7 +196,7 @@ static int oocd_trace_read_trace(etm_con
else
num_frames = address;
- /* read data into temporary array for unpacking
+ /* read data into temporary array for unpacking
* one frame from OpenOCD+trace corresponds to 16 trace cycles
*/
trace_data = malloc(sizeof(u8) * num_frames * 16);
@@ -204,13 +209,13 @@ static int oocd_trace_read_trace(etm_con
etm_ctx->trace_depth = num_frames * 16;
etm_ctx->trace_data = malloc(sizeof(etmv1_trace_data_t) * etm_ctx->trace_depth);
-
+
for (i = 0; i < num_frames * 16; i++)
{
etm_ctx->trace_data[i].pipestat = (trace_data[i] & 0x7);
etm_ctx->trace_data[i].packet = (trace_data[i] & 0x78) >> 3;
etm_ctx->trace_data[i].flags = 0;
-
+
if ((trace_data[i] & 0x80) >> 7)
{
etm_ctx->trace_data[i].flags |= ETMV1_TRACESYNC_CYCLE;
@@ -222,7 +227,7 @@ static int oocd_trace_read_trace(etm_con
etm_ctx->trace_data[i].flags |= ETMV1_TRIGGER_CYCLE;
}
}
-
+
free(trace_data);
return ERROR_OK;
@@ -233,19 +238,19 @@ static int oocd_trace_start_capture(etm_
oocd_trace_t *oocd_trace = etm_ctx->capture_driver_priv;
u32 control = 0x1; /* 0x1: enabled */
u32 trigger_count;
-
+
if (((etm_ctx->portmode & ETM_PORT_MODE_MASK) != ETM_PORT_NORMAL)
|| ((etm_ctx->portmode & ETM_PORT_WIDTH_MASK) != ETM_PORT_4BIT))
{
LOG_DEBUG("OpenOCD+trace only supports normal 4-bit ETM mode");
return ERROR_ETM_PORTMODE_NOT_SUPPORTED;
}
-
+
if ((etm_ctx->portmode & ETM_PORT_CLOCK_MASK) == ETM_PORT_HALF_CLOCK)
{
control |= 0x2; /* half rate clock, capture at twice the clock rate */
}
-
+
/* OpenOCD+trace holds up to 16 million samples,
* but trigger counts is set in multiples of 16 */
trigger_count = (1048576 * etm_ctx->trigger_percent) / 100;
@@ -254,22 +259,22 @@ static int oocd_trace_start_capture(etm_
oocd_trace_write_reg(oocd_trace, OOCD_TRACE_ADDRESS, 0x0);
oocd_trace_write_reg(oocd_trace, OOCD_TRACE_TRIGGER_COUNTER, trigger_count);
oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, control);
-
+
/* we're starting a new trace, initialize capture status */
etm_ctx->capture_status = TRACE_RUNNING;
-
- return ERROR_OK;
+
+ return ERROR_OK;
}
static int oocd_trace_stop_capture(etm_context_t *etm_ctx)
{
oocd_trace_t *oocd_trace = etm_ctx->capture_driver_priv;
- /* trace stopped, just clear running flag, but preserve others */
+ /* trace stopped, just clear running flag, but preserve others */
etm_ctx->capture_status &= ~TRACE_RUNNING;
-
+
oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, 0x0);
-
+
return ERROR_OK;
}
@@ -289,28 +294,28 @@ static int handle_oocd_trace_config_comm
target_t *target;
armv4_5_common_t *armv4_5;
arm7_9_common_t *arm7_9;
-
+
if (argc != 2)
{
LOG_ERROR("incomplete 'oocd_trace config <target> <tty>' command");
exit(-1);
}
-
+
target = get_current_target(cmd_ctx);
-
+
if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
{
command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
return ERROR_OK;
}
-
+
if (arm7_9->etm_ctx)
{
oocd_trace_t *oocd_trace = malloc(sizeof(oocd_trace_t));
-
+
arm7_9->etm_ctx->capture_driver_priv = oocd_trace;
oocd_trace->etm_ctx = arm7_9->etm_ctx;
-
+
/* copy name of TTY device used to communicate with OpenOCD+trace */
oocd_trace->tty = strndup(args[1], 256);
}
@@ -329,36 +334,36 @@ static int handle_oocd_trace_status_comm
arm7_9_common_t *arm7_9;
oocd_trace_t *oocd_trace;
u32 status;
-
+
target = get_current_target(cmd_ctx);
-
+
if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
{
command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
return ERROR_OK;
}
-
+
if (!arm7_9->etm_ctx)
{
command_print(cmd_ctx, "current target doesn't have an ETM configured");
return ERROR_OK;
}
-
+
if (strcmp(arm7_9->etm_ctx->capture_driver->name, "oocd_trace") != 0)
{
command_print(cmd_ctx, "current target's ETM capture driver isn't 'oocd_trace'");
return ERROR_OK;
}
-
+
oocd_trace = (oocd_trace_t*)arm7_9->etm_ctx->capture_driver_priv;
-
+
oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
-
+
if (status & 0x8)
command_print(cmd_ctx, "trace clock locked");
else
command_print(cmd_ctx, "no trace clock");
-
+
return ERROR_OK;
}
@@ -370,33 +375,33 @@ static int handle_oocd_trace_resync_comm
oocd_trace_t *oocd_trace;
size_t bytes_written;
u8 cmd_array[1];
-
+
target = get_current_target(cmd_ctx);
-
+
if (arm7_9_get_arch_pointers(target, &armv4_5, &arm7_9) != ERROR_OK)
{
command_print(cmd_ctx, "current target isn't an ARM7/ARM9 target");
return ERROR_OK;
}
-
+
if (!arm7_9->etm_ctx)
{
command_print(cmd_ctx, "current target doesn't have an ETM configured");
return ERROR_OK;
}
-
+
if (strcmp(arm7_9->etm_ctx->capture_driver->name, "oocd_trace") != 0)
{
command_print(cmd_ctx, "current target's ETM capture driver isn't 'oocd_trace'");
return ERROR_OK;
}
-
+
oocd_trace = (oocd_trace_t*)arm7_9->etm_ctx->capture_driver_priv;
-
+
cmd_array[0] = 0xf0;
bytes_written = write(oocd_trace->tty_fd, cmd_array, 1);
-
+
command_print(cmd_ctx, "requesting traceclock resync");
LOG_DEBUG("resyncing traceclk pll");
@@ -406,9 +411,9 @@ static int handle_oocd_trace_resync_comm
int oocd_trace_register_commands(struct command_context_s *cmd_ctx)
{
command_t *oocd_trace_cmd;
-
+
oocd_trace_cmd = register_command(cmd_ctx, NULL, "oocd_trace", NULL, COMMAND_ANY, "OpenOCD+trace");
-
+
register_command(cmd_ctx, oocd_trace_cmd, "config", handle_oocd_trace_config_command, COMMAND_CONFIG, NULL);
register_command(cmd_ctx, oocd_trace_cmd, "status", handle_oocd_trace_status_command, COMMAND_EXEC, "display OpenOCD+trace status");
_______________________________________________
Openocd-development mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/openocd-development