aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/openocd.texi179
-rw-r--r--src/flash/nor/esirisc_flash.c29
-rw-r--r--src/target/Makefile.am6
-rw-r--r--src/target/esirisc.c192
-rw-r--r--src/target/esirisc.h12
-rw-r--r--src/target/esirisc_jtag.c15
-rw-r--r--src/target/esirisc_jtag.h1
-rw-r--r--src/target/esirisc_trace.c1203
-rw-r--r--src/target/esirisc_trace.h105
9 files changed, 1641 insertions, 101 deletions
diff --git a/doc/openocd.texi b/doc/openocd.texi
index d878da0..11ee93e 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -5723,16 +5723,17 @@ configuration register interface, @option{clock_hz} is the expected clock
frequency, and @option{wait_states} is the number of configured read wait states.
@example
-flash bank $_FLASHNAME esirisc base_address size_bytes 0 0 $_TARGETNAME cfg_address clock_hz wait_states
+flash bank $_FLASHNAME esirisc base_address size_bytes 0 0 \
+ $_TARGETNAME cfg_address clock_hz wait_states
@end example
-@deffn Command {esirisc_flash mass_erase} (bank_id)
-Erases all pages in data memory for the bank identified by @option{bank_id}.
+@deffn Command {esirisc flash mass_erase} bank_id
+Erase all pages in data memory for the bank identified by @option{bank_id}.
@end deffn
-@deffn Command {esirisc_flash ref_erase} (bank_id)
-Erases the reference cell for the bank identified by @option{bank_id}. This is
-an uncommon operation.
+@deffn Command {esirisc flash ref_erase} bank_id
+Erase the reference cell for the bank identified by @option{bank_id}. @emph{This
+is an uncommon operation.}
@end deffn
@end deffn
@@ -9041,17 +9042,13 @@ Selects whether interrupts will be processed when single stepping. The default c
eSi-RISC is a highly configurable microprocessor architecture for embedded systems
provided by EnSilica. (See: @url{http://www.ensilica.com/risc-ip/}.)
-@subsection esirisc specific commands
+@subsection eSi-RISC Configuration
+
@deffn Command {esirisc cache_arch} (@option{harvard}|@option{von_neumann})
Configure the caching architecture. Targets with the @code{UNIFIED_ADDRESS_SPACE}
option disabled employ a Harvard architecture. By default, @option{von_neumann} is assumed.
@end deffn
-@deffn Command {esirisc flush_caches}
-Flush instruction and data caches. This command requires that the target is halted
-when the command is issued and configured with an instruction or data cache.
-@end deffn
-
@deffn Command {esirisc hwdc} (@option{all}|@option{none}|mask ...)
Configure hardware debug control. The HWDC register controls which exceptions return
control back to the debugger. Possible masks are @option{all}, @option{none},
@@ -9059,6 +9056,164 @@ control back to the debugger. Possible masks are @option{all}, @option{none},
By default, @option{reset}, @option{error}, and @option{debug} are enabled.
@end deffn
+@subsection eSi-RISC Operation
+
+@deffn Command {esirisc flush_caches}
+Flush instruction and data caches. This command requires that the target is halted
+when the command is issued and configured with an instruction or data cache.
+@end deffn
+
+@subsection eSi-Trace Configuration
+
+eSi-RISC targets may be configured with support for instruction tracing. Trace
+data may be written to an in-memory buffer or FIFO. If a FIFO is configured, DMA
+is typically employed to move trace data off-device using a high-speed
+peripheral (eg. SPI). Collected trace data is encoded in one of three different
+formats. At a minimum, @command{esirisc trace buffer} or @command{esirisc trace
+fifo} must be issued along with @command{esirisc trace format} before trace data
+can be collected.
+
+OpenOCD provides rudimentary analysis of collected trace data. If more detail is
+needed, collected trace data can be dumped to a file and processed by external
+tooling.
+
+@quotation Issues
+OpenOCD is unable to process trace data sent to a FIFO. A potential workaround
+for this issue is to configure DMA to copy trace data to an in-memory buffer,
+which can then be passed to the @command{esirisc trace analyze} and
+@command{esirisc trace dump} commands.
+
+It is possible to corrupt trace data when using a FIFO if the peripheral
+responsible for draining data from the FIFO is not fast enough. This can be
+managed by enabling flow control, however this can impact timing-sensitive
+software operation on the CPU.
+@end quotation
+
+@deffn Command {esirisc trace buffer} address size [@option{wrap}]
+Configure trace buffer using the provided address and size. If the @option{wrap}
+option is specified, trace collection will continue once the end of the buffer
+is reached. By default, wrap is disabled.
+@end deffn
+
+@deffn Command {esirisc trace fifo} address
+Configure trace FIFO using the provided address.
+@end deffn
+
+@deffn Command {esirisc trace flow_control} (@option{enable}|@option{disable})
+Enable or disable stalling the CPU to collect trace data. By default, flow
+control is disabled.
+@end deffn
+
+@deffn Command {esirisc trace format} (@option{full}|@option{branch}|@option{icache}) pc_bits
+Configure trace format and number of PC bits to be captured. @option{pc_bits}
+must be within 1 and 31 as the LSB is not collected. If external tooling is used
+to analyze collected trace data, these values must match.
+
+Supported trace formats:
+@itemize
+@item @option{full} capture full trace data, allowing execution history and
+timing to be determined.
+@item @option{branch} capture taken branch instructions and branch target
+addresses.
+@item @option{icache} capture instruction cache misses.
+@end itemize
+@end deffn
+
+@deffn Command {esirisc trace trigger start} (@option{condition}) [start_data start_mask]
+Configure trigger start condition using the provided start data and mask. A
+brief description of each condition is provided below; for more detail on how
+these values are used, see the eSi-RISC Architecture Manual.
+
+Supported conditions:
+@itemize
+@item @option{none} manual tracing (see @command{esirisc trace start}).
+@item @option{pc} start tracing if the PC matches start data and mask.
+@item @option{load} start tracing if the effective address of a load
+instruction matches start data and mask.
+@item @option{store} start tracing if the effective address of a store
+instruction matches start data and mask.
+@item @option{exception} start tracing if the EID of an exception matches start
+data and mask.
+@item @option{eret} start tracing when an @code{ERET} instruction is executed.
+@item @option{wait} start tracing when a @code{WAIT} instruction is executed.
+@item @option{stop} start tracing when a @code{STOP} instruction is executed.
+@item @option{high} start tracing when an external signal is a logical high.
+@item @option{low} start tracing when an external signal is a logical low.
+@end itemize
+@end deffn
+
+@deffn Command {esirisc trace trigger stop} (@option{condition}) [stop_data stop_mask]
+Configure trigger stop condition using the provided stop data and mask. A brief
+description of each condition is provided below; for more detail on how these
+values are used, see the eSi-RISC Architecture Manual.
+
+Supported conditions:
+@itemize
+@item @option{none} manual tracing (see @command{esirisc trace stop}).
+@item @option{pc} stop tracing if the PC matches stop data and mask.
+@item @option{load} stop tracing if the effective address of a load
+instruction matches stop data and mask.
+@item @option{store} stop tracing if the effective address of a store
+instruction matches stop data and mask.
+@item @option{exception} stop tracing if the EID of an exception matches stop
+data and mask.
+@item @option{eret} stop tracing when an @code{ERET} instruction is executed.
+@item @option{wait} stop tracing when a @code{WAIT} instruction is executed.
+@item @option{stop} stop tracing when a @code{STOP} instruction is executed.
+@end itemize
+@end deffn
+
+@deffn Command {esirisc trace trigger delay} (@option{trigger}) [cycles]
+Configure trigger start/stop delay in clock cycles.
+
+Supported triggers:
+@itemize
+@item @option{none} no delay to start or stop collection.
+@item @option{start} delay @option{cycles} after trigger to start collection.
+@item @option{stop} delay @option{cycles} after trigger to stop collection.
+@item @option{both} delay @option{cycles} after both triggers to start or stop
+collection.
+@end itemize
+@end deffn
+
+@subsection eSi-Trace Operation
+
+@deffn Command {esirisc trace init}
+Initialize trace collection. This command must be called any time the
+configuration changes. If an trace buffer has been configured, the contents will
+be overwritten when trace collection starts.
+@end deffn
+
+@deffn Command {esirisc trace info}
+Display trace configuration.
+@end deffn
+
+@deffn Command {esirisc trace status}
+Display trace collection status.
+@end deffn
+
+@deffn Command {esirisc trace start}
+Start manual trace collection.
+@end deffn
+
+@deffn Command {esirisc trace stop}
+Stop manual trace collection.
+@end deffn
+
+@deffn Command {esirisc trace analyze} [address size]
+Analyze collected trace data. This command may only be used if a trace buffer
+has been configured. If a trace FIFO has been configured, trace data must be
+copied to an in-memory buffer identified by the @option{address} and
+@option{size} options using DMA.
+@end deffn
+
+@deffn Command {esirisc trace dump} [address size] @file{filename}
+Dump collected trace data to file. This command may only be used if a trace
+buffer has been configured. If a trace FIFO has been configured, trace data must
+be copied to an in-memory buffer identified by the @option{address} and
+@option{size} options using DMA.
+@end deffn
+
@section Intel Architecture
Intel Quark X10xx is the first product in the Quark family of SoCs. It is an IA-32
diff --git a/src/flash/nor/esirisc_flash.c b/src/flash/nor/esirisc_flash.c
index f3833df..8ecd27a 100644
--- a/src/flash/nor/esirisc_flash.c
+++ b/src/flash/nor/esirisc_flash.c
@@ -104,9 +104,12 @@ struct esirisc_flash_bank {
uint32_t wait_states;
};
+static const struct command_registration esirisc_flash_command_handlers[];
+
FLASH_BANK_COMMAND_HANDLER(esirisc_flash_bank_command)
{
struct esirisc_flash_bank *esirisc_info;
+ struct command *esirisc_cmd;
if (CMD_ARGC < 9)
return ERROR_COMMAND_SYNTAX_ERROR;
@@ -119,6 +122,10 @@ FLASH_BANK_COMMAND_HANDLER(esirisc_flash_bank_command)
bank->driver_priv = esirisc_info;
+ /* register commands using existing esirisc context */
+ esirisc_cmd = command_find_in_context(CMD_CTX, "esirisc");
+ register_commands(CMD_CTX, esirisc_cmd, esirisc_flash_command_handlers);
+
return ERROR_OK;
}
@@ -435,8 +442,8 @@ static int esirisc_flash_init(struct flash_bank *bank)
esirisc_flash_disable_protect(bank);
/* initialize timing registers */
- value = TIMING0_F(esirisc_flash_num_cycles(bank, TNVH)) |
- TIMING0_R(esirisc_info->wait_states);
+ value = TIMING0_F(esirisc_flash_num_cycles(bank, TNVH))
+ | TIMING0_R(esirisc_info->wait_states);
LOG_DEBUG("TIMING0: 0x%" PRIx32, value);
target_write_u32(target, esirisc_info->cfg + TIMING0, value);
@@ -446,9 +453,9 @@ static int esirisc_flash_init(struct flash_bank *bank)
LOG_DEBUG("TIMING1: 0x%" PRIx32, value);
target_write_u32(target, esirisc_info->cfg + TIMING1, value);
- value = TIMING2_T(esirisc_flash_num_cycles(bank, 10)) |
- TIMING2_H(esirisc_flash_num_cycles(bank, 100)) |
- TIMING2_P(esirisc_flash_num_cycles(bank, TPROG));
+ value = TIMING2_T(esirisc_flash_num_cycles(bank, 10))
+ | TIMING2_H(esirisc_flash_num_cycles(bank, 100))
+ | TIMING2_P(esirisc_flash_num_cycles(bank, TPROG));
LOG_DEBUG("TIMING2: 0x%" PRIx32, value);
target_write_u32(target, esirisc_info->cfg + TIMING2, value);
@@ -579,14 +586,14 @@ static const struct command_registration esirisc_flash_exec_command_handlers[] =
.name = "mass_erase",
.handler = handle_esirisc_flash_mass_erase_command,
.mode = COMMAND_EXEC,
- .help = "erases all pages in data memory",
+ .help = "erase all pages in data memory",
.usage = "bank_id",
},
{
.name = "ref_erase",
.handler = handle_esirisc_flash_ref_erase_command,
.mode = COMMAND_EXEC,
- .help = "erases reference cell (uncommon)",
+ .help = "erase reference cell (uncommon)",
.usage = "bank_id",
},
COMMAND_REGISTRATION_DONE
@@ -594,9 +601,9 @@ static const struct command_registration esirisc_flash_exec_command_handlers[] =
static const struct command_registration esirisc_flash_command_handlers[] = {
{
- .name = "esirisc_flash",
- .mode = COMMAND_ANY,
- .help = "eSi-RISC flash command group",
+ .name = "flash",
+ .mode = COMMAND_EXEC,
+ .help = "eSi-TSMC Flash command group",
.usage = "",
.chain = esirisc_flash_exec_command_handlers,
},
@@ -605,7 +612,6 @@ static const struct command_registration esirisc_flash_command_handlers[] = {
struct flash_driver esirisc_flash = {
.name = "esirisc",
- .commands = esirisc_flash_command_handlers,
.usage = "flash bank bank_id 'esirisc' base_address size_bytes 0 0 target "
"cfg_address clock_hz wait_states",
.flash_bank_command = esirisc_flash_bank_command,
@@ -618,4 +624,5 @@ struct flash_driver esirisc_flash = {
.erase_check = default_flash_blank_check,
.protect_check = esirisc_flash_protect_check,
.info = esirisc_flash_info,
+ .free_driver_priv = default_flash_free_driver_priv,
};
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 8e9fcb2..afa5f49 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -143,7 +143,8 @@ INTEL_IA32_SRC = \
ESIRISC_SRC = \
%D%/esirisc.c \
- %D%/esirisc_jtag.c
+ %D%/esirisc_jtag.c \
+ %D%/esirisc_trace.c
%C%_libtarget_la_SOURCES += \
%D%/algorithm.h \
@@ -228,7 +229,8 @@ ESIRISC_SRC = \
%D%/arm_cti.h \
%D%/esirisc.h \
%D%/esirisc_jtag.h \
- %D%/esirisc_regs.h
+ %D%/esirisc_regs.h \
+ %D%/esirisc_trace.h
include %D%/openrisc/Makefile.am
include %D%/riscv/Makefile.am
diff --git a/src/target/esirisc.c b/src/target/esirisc.c
index 38100e8..3d2954f 100644
--- a/src/target/esirisc.c
+++ b/src/target/esirisc.c
@@ -34,27 +34,60 @@
#include "esirisc.h"
-#define RESET_TIMEOUT 5000 /* 5s */
-#define STEP_TIMEOUT 1000 /* 1s */
+#define RESET_TIMEOUT 5000 /* 5s */
+#define STEP_TIMEOUT 1000 /* 1s */
/*
* eSi-RISC targets support a configurable number of interrupts;
* up to 32 interrupts are supported.
*/
-static const char * const esirisc_exceptions[] = {
- "Reset", "HardwareFailure", "NMI", "InstBreakpoint", "DataBreakpoint",
- "Unsupported", "PrivilegeViolation", "InstBusError", "DataBusError",
- "AlignmentError", "ArithmeticError", "SystemCall", "MemoryManagement",
- "Unrecoverable", "Reserved",
-
- "Interrupt0", "Interrupt1", "Interrupt2", "Interrupt3",
- "Interrupt4", "Interrupt5", "Interrupt6", "Interrupt7",
- "Interrupt8", "Interrupt9", "Interrupt10", "Interrupt11",
- "Interrupt12", "Interrupt13", "Interrupt14", "Interrupt15",
- "Interrupt16", "Interrupt17", "Interrupt18", "Interrupt19",
- "Interrupt20", "Interrupt21", "Interrupt22", "Interrupt23",
- "Interrupt24", "Interrupt25", "Interrupt26", "Interrupt27",
- "Interrupt28", "Interrupt29", "Interrupt30", "Interrupt31",
+static const char * const esirisc_exception_strings[] = {
+ [EID_RESET] = "Reset",
+ [EID_HARDWARE_FAILURE] = "HardwareFailure",
+ [EID_NMI] = "NMI",
+ [EID_INST_BREAKPOINT] = "InstBreakpoint",
+ [EID_DATA_BREAKPOINT] = "DataBreakpoint",
+ [EID_UNSUPPORTED] = "Unsupported",
+ [EID_PRIVILEGE_VIOLATION] = "PrivilegeViolation",
+ [EID_INST_BUS_ERROR] = "InstBusError",
+ [EID_DATA_BUS_ERROR] = "DataBusError",
+ [EID_ALIGNMENT_ERROR] = "AlignmentError",
+ [EID_ARITHMETIC_ERROR] = "ArithmeticError",
+ [EID_SYSTEM_CALL] = "SystemCall",
+ [EID_MEMORY_MANAGEMENT] = "MemoryManagement",
+ [EID_UNRECOVERABLE] = "Unrecoverable",
+ [EID_INTERRUPTn+0] = "Interrupt0",
+ [EID_INTERRUPTn+1] = "Interrupt1",
+ [EID_INTERRUPTn+2] = "Interrupt2",
+ [EID_INTERRUPTn+3] = "Interrupt3",
+ [EID_INTERRUPTn+4] = "Interrupt4",
+ [EID_INTERRUPTn+5] = "Interrupt5",
+ [EID_INTERRUPTn+6] = "Interrupt6",
+ [EID_INTERRUPTn+7] = "Interrupt7",
+ [EID_INTERRUPTn+8] = "Interrupt8",
+ [EID_INTERRUPTn+9] = "Interrupt9",
+ [EID_INTERRUPTn+10] = "Interrupt10",
+ [EID_INTERRUPTn+11] = "Interrupt11",
+ [EID_INTERRUPTn+12] = "Interrupt12",
+ [EID_INTERRUPTn+13] = "Interrupt13",
+ [EID_INTERRUPTn+14] = "Interrupt14",
+ [EID_INTERRUPTn+15] = "Interrupt15",
+ [EID_INTERRUPTn+16] = "Interrupt16",
+ [EID_INTERRUPTn+17] = "Interrupt17",
+ [EID_INTERRUPTn+18] = "Interrupt18",
+ [EID_INTERRUPTn+19] = "Interrupt19",
+ [EID_INTERRUPTn+20] = "Interrupt20",
+ [EID_INTERRUPTn+21] = "Interrupt21",
+ [EID_INTERRUPTn+22] = "Interrupt22",
+ [EID_INTERRUPTn+23] = "Interrupt23",
+ [EID_INTERRUPTn+24] = "Interrupt24",
+ [EID_INTERRUPTn+25] = "Interrupt25",
+ [EID_INTERRUPTn+26] = "Interrupt26",
+ [EID_INTERRUPTn+27] = "Interrupt27",
+ [EID_INTERRUPTn+28] = "Interrupt28",
+ [EID_INTERRUPTn+29] = "Interrupt29",
+ [EID_INTERRUPTn+30] = "Interrupt30",
+ [EID_INTERRUPTn+31] = "Interrupt31",
};
/*
@@ -142,7 +175,7 @@ static int esirisc_disable_interrupts(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: ETC", target_name(target));
+ LOG_ERROR("%s: failed to read Thread CSR: ETC", target_name(target));
return retval;
}
@@ -150,7 +183,7 @@ static int esirisc_disable_interrupts(struct target *target)
retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: ETC", target_name(target));
+ LOG_ERROR("%s: failed to write Thread CSR: ETC", target_name(target));
return retval;
}
@@ -169,7 +202,7 @@ static int esirisc_enable_interrupts(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, &etc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: ETC", target_name(target));
+ LOG_ERROR("%s: failed to read Thread CSR: ETC", target_name(target));
return retval;
}
@@ -177,7 +210,7 @@ static int esirisc_enable_interrupts(struct target *target)
retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC, etc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: ETC", target_name(target));
+ LOG_ERROR("%s: failed to write Thread CSR: ETC", target_name(target));
return retval;
}
@@ -195,7 +228,7 @@ static int esirisc_save_interrupts(struct target *target)
int retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC,
&esirisc->etc_save);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: ETC", target_name(target));
+ LOG_ERROR("%s: failed to read Thread CSR: ETC", target_name(target));
return retval;
}
@@ -212,7 +245,7 @@ static int esirisc_restore_interrupts(struct target *target)
int retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETC,
esirisc->etc_save);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: ETC", target_name(target));
+ LOG_ERROR("%s: failed to write Thread CSR: ETC", target_name(target));
return retval;
}
@@ -230,7 +263,7 @@ static int esirisc_save_hwdc(struct target *target)
int retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC,
&esirisc->hwdc_save);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: HWDC", target_name(target));
+ LOG_ERROR("%s: failed to read Thread CSR: HWDC", target_name(target));
return retval;
}
@@ -248,7 +281,7 @@ static int esirisc_restore_hwdc(struct target *target)
int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_HWDC,
esirisc->hwdc_save);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: HWDC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: HWDC", target_name(target));
return retval;
}
@@ -478,14 +511,14 @@ static int esirisc_add_breakpoint(struct target *target, struct breakpoint *brea
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBAn + bp_index,
breakpoint->address);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: IBA", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: IBA", target_name(target));
return retval;
}
/* enable instruction breakpoint */
retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: IBC", target_name(target));
+ LOG_ERROR("%s: failed to read Debug CSR: IBC", target_name(target));
return retval;
}
@@ -493,7 +526,7 @@ static int esirisc_add_breakpoint(struct target *target, struct breakpoint *brea
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: IBC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: IBC", target_name(target));
return retval;
}
@@ -529,7 +562,7 @@ static int esirisc_remove_breakpoint(struct target *target, struct breakpoint *b
/* disable instruction breakpoint */
retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, &ibc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: IBC", target_name(target));
+ LOG_ERROR("%s: failed to read Debug CSR: IBC", target_name(target));
return retval;
}
@@ -537,7 +570,7 @@ static int esirisc_remove_breakpoint(struct target *target, struct breakpoint *b
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, ibc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: IBC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: IBC", target_name(target));
return retval;
}
@@ -557,7 +590,7 @@ static int esirisc_remove_breakpoints(struct target *target)
/* clear instruction breakpoints */
int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_IBC, 0);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: IBC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: IBC", target_name(target));
return retval;
}
@@ -604,14 +637,14 @@ static int esirisc_add_watchpoint(struct target *target, struct watchpoint *watc
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBAn + wp_index,
watchpoint->address);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: DBA", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: DBA", target_name(target));
return retval;
}
/* specify data breakpoint size */
retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, &dbs);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: DBS", target_name(target));
+ LOG_ERROR("%s: failed to read Debug CSR: DBS", target_name(target));
return retval;
}
@@ -642,14 +675,14 @@ static int esirisc_add_watchpoint(struct target *target, struct watchpoint *watc
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBS, dbs);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: DBS", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: DBS", target_name(target));
return retval;
}
/* enable data breakpoint */
retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: DBC", target_name(target));
+ LOG_ERROR("%s: failed to read Debug CSR: DBC", target_name(target));
return retval;
}
@@ -677,7 +710,7 @@ static int esirisc_add_watchpoint(struct target *target, struct watchpoint *watc
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: DBC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: DBC", target_name(target));
return retval;
}
@@ -713,7 +746,7 @@ static int esirisc_remove_watchpoint(struct target *target, struct watchpoint *w
/* disable data breakpoint */
retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, &dbc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: DBC", target_name(target));
+ LOG_ERROR("%s: failed to read Debug CSR: DBC", target_name(target));
return retval;
}
@@ -721,7 +754,7 @@ static int esirisc_remove_watchpoint(struct target *target, struct watchpoint *w
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, dbc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: DBC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: DBC", target_name(target));
return retval;
}
@@ -741,7 +774,7 @@ static int esirisc_remove_watchpoints(struct target *target)
/* clear data breakpoints */
int retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DBC, 0);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: DBC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: DBC", target_name(target));
return retval;
}
@@ -782,7 +815,7 @@ static int esirisc_disable_step(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: DC", target_name(target));
+ LOG_ERROR("%s: failed to read Debug CSR: DC", target_name(target));
return retval;
}
@@ -790,7 +823,7 @@ static int esirisc_disable_step(struct target *target)
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: DC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: DC", target_name(target));
return retval;
}
@@ -808,7 +841,7 @@ static int esirisc_enable_step(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, &dc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: DC", target_name(target));
+ LOG_ERROR("%s: failed to read Debug CSR: DC", target_name(target));
return retval;
}
@@ -816,7 +849,7 @@ static int esirisc_enable_step(struct target *target)
retval = esirisc_jtag_write_csr(jtag_info, CSR_DEBUG, CSR_DEBUG_DC, dc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: DC", target_name(target));
+ LOG_ERROR("%s: failed to write Debug CSR: DC", target_name(target));
return retval;
}
@@ -1132,7 +1165,7 @@ static int esirisc_reset_entry(struct target *target)
/* read exception table address */
retval = esirisc_jtag_read_csr(jtag_info, CSR_THREAD, CSR_THREAD_ETA, &eta);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: ETA", target_name(target));
+ LOG_ERROR("%s: failed to read Thread CSR: ETA", target_name(target));
return retval;
}
@@ -1147,7 +1180,7 @@ static int esirisc_reset_entry(struct target *target)
/* write reset entry point */
retval = esirisc_jtag_write_csr(jtag_info, CSR_THREAD, CSR_THREAD_EPC, epc);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to write CSR: EPC", target_name(target));
+ LOG_ERROR("%s: failed to write Thread CSR: EPC", target_name(target));
return retval;
}
@@ -1215,15 +1248,9 @@ static int esirisc_arch_state(struct target *target)
uint32_t eid = buf_get_u32(esirisc->eid->value, 0, esirisc->eid->size);
uint32_t ed = buf_get_u32(esirisc->ed->value, 0, esirisc->ed->size);
- LOG_DEBUG("-");
-
- const char *exception = "Unknown";
- if (eid < ARRAY_SIZE(esirisc_exceptions))
- exception = esirisc_exceptions[eid];
-
LOG_USER("target halted due to %s, exception: %s\n"
- "EPC: 0x%" PRIx32 " ECAS: 0x%" PRIx32 " EID: 0x%" PRIx32 " ED: 0x%" PRIx32,
- debug_reason_name(target), exception, epc, ecas, eid, ed);
+ "EPC: 0x%" PRIx32 ", ECAS: 0x%" PRIx32 ", EID: 0x%" PRIx32 ", ED: 0x%" PRIx32,
+ debug_reason_name(target), esirisc_exception_strings[eid], epc, ecas, eid, ed);
return ERROR_OK;
}
@@ -1242,7 +1269,7 @@ static const char *esirisc_get_gdb_arch(struct target *target)
*/
if (esirisc->gdb_arch == NULL && target_was_examined(target))
esirisc->gdb_arch = alloc_printf("esirisc:%d_bit_%d_reg_%s",
- esirisc->num_bits, esirisc->num_regs, esirisc_cache_arch(esirisc));
+ esirisc->num_bits, esirisc->num_regs, esirisc_cache_arch_name(esirisc));
return esirisc->gdb_arch;
}
@@ -1477,7 +1504,7 @@ static int esirisc_identify(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_ARCH0, &csr);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: ARCH0", target_name(target));
+ LOG_ERROR("%s: failed to read Configuration CSR: ARCH0", target_name(target));
return retval;
}
@@ -1486,7 +1513,7 @@ static int esirisc_identify(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_MEM, &csr);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: MEM", target_name(target));
+ LOG_ERROR("%s: failed to read Configuration CSR: MEM", target_name(target));
return retval;
}
@@ -1495,7 +1522,7 @@ static int esirisc_identify(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_IC, &csr);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: IC", target_name(target));
+ LOG_ERROR("%s: failed to read Configuration CSR: IC", target_name(target));
return retval;
}
@@ -1503,7 +1530,7 @@ static int esirisc_identify(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DC, &csr);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: DC", target_name(target));
+ LOG_ERROR("%s: failed to read Configuration CSR: DC", target_name(target));
return retval;
}
@@ -1511,13 +1538,21 @@ static int esirisc_identify(struct target *target)
retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_DBG, &csr);
if (retval != ERROR_OK) {
- LOG_ERROR("%s: failed to read CSR: DBG", target_name(target));
+ LOG_ERROR("%s: failed to read Configuration CSR: DBG", target_name(target));
return retval;
}
esirisc->num_breakpoints = (csr >> 7) & 0xf; /* DBG.BP */
esirisc->num_watchpoints = (csr >> 12) & 0xf; /* DBG.WP */
+ retval = esirisc_jtag_read_csr(jtag_info, CSR_CONFIG, CSR_CONFIG_TRACE, &csr);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to read Configuration CSR: TRACE", target_name(target));
+ return retval;
+ }
+
+ esirisc->has_trace = !!(csr & 1<<0); /* TRACE.T */
+
return ERROR_OK;
}
@@ -1616,13 +1651,14 @@ static int esirisc_examine(struct target *target)
target_set_examined(target);
LOG_INFO("%s: %d bit, %d registers, %s%s%s", target_name(target),
- esirisc->num_bits, esirisc->num_regs,
- target_endianness(target),
- esirisc->has_icache ? ", icache" : "",
- esirisc->has_dcache ? ", dcache" : "");
+ esirisc->num_bits, esirisc->num_regs,
+ target_endianness(target),
+ esirisc->has_icache ? ", icache" : "",
+ esirisc->has_dcache ? ", dcache" : "");
- LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints", target_name(target),
- esirisc->num_breakpoints, esirisc->num_watchpoints);
+ LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints%s", target_name(target),
+ esirisc->num_breakpoints, esirisc->num_watchpoints,
+ esirisc->has_trace ? ", trace" : "");
}
return ERROR_OK;
@@ -1644,7 +1680,7 @@ COMMAND_HANDLER(handle_esirisc_cache_arch_command)
}
}
- command_print(CMD_CTX, "esirisc cache_arch %s", esirisc_cache_arch(esirisc));
+ command_print(CMD_CTX, "esirisc cache_arch %s", esirisc_cache_arch_name(esirisc));
return ERROR_OK;
}
@@ -1720,19 +1756,23 @@ COMMAND_HANDLER(handle_esirisc_hwdc_command)
static const struct command_registration esirisc_exec_command_handlers[] = {
{
- .name = "cache_arch",
- .handler = handle_esirisc_cache_arch_command,
- .mode = COMMAND_ANY,
- .help = "configure cache architecture",
- .usage = "['harvard'|'von_neumann']",
- },
- {
.name = "flush_caches",
.handler = handle_esirisc_flush_caches_command,
.mode = COMMAND_EXEC,
.help = "flush instruction and data caches",
.usage = "",
},
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration esirisc_any_command_handlers[] = {
+ {
+ .name = "cache_arch",
+ .handler = handle_esirisc_cache_arch_command,
+ .mode = COMMAND_ANY,
+ .help = "configure cache architecture",
+ .usage = "['harvard'|'von_neumann']",
+ },
{
.name = "hwdc",
.handler = handle_esirisc_hwdc_command,
@@ -1740,6 +1780,12 @@ static const struct command_registration esirisc_exec_command_handlers[] = {
.help = "configure hardware debug control",
.usage = "['all'|'none'|mask ...]",
},
+ {
+ .chain = esirisc_exec_command_handlers
+ },
+ {
+ .chain = esirisc_trace_command_handlers
+ },
COMMAND_REGISTRATION_DONE
};
@@ -1749,7 +1795,7 @@ static const struct command_registration esirisc_command_handlers[] = {
.mode = COMMAND_ANY,
.help = "eSi-RISC command group",
.usage = "",
- .chain = esirisc_exec_command_handlers,
+ .chain = esirisc_any_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
diff --git a/src/target/esirisc.h b/src/target/esirisc.h
index bb50652..57deba6 100644
--- a/src/target/esirisc.h
+++ b/src/target/esirisc.h
@@ -20,12 +20,14 @@
#ifndef OPENOCD_TARGET_ESIRISC_H
#define OPENOCD_TARGET_ESIRISC_H
+#include <helper/types.h>
#include <target/breakpoints.h>
#include <target/register.h>
#include <target/target.h>
#include "esirisc_jtag.h"
#include "esirisc_regs.h"
+#include "esirisc_trace.h"
#define MAX_BREAKPOINTS 8
#define MAX_WATCHPOINTS 8
@@ -88,11 +90,15 @@ struct esirisc_common {
int num_regs;
bool has_icache;
bool has_dcache;
- int num_breakpoints;
- int num_watchpoints;
+ bool has_trace;
+ int num_breakpoints;
struct breakpoint *breakpoints_p[MAX_BREAKPOINTS];
+
+ int num_watchpoints;
struct watchpoint *watchpoints_p[MAX_WATCHPOINTS];
+
+ struct esirisc_trace trace_info;
};
union esirisc_memory {
@@ -116,7 +122,7 @@ static inline struct esirisc_common *target_to_esirisc(struct target *target)
return (struct esirisc_common *)target->arch_info;
}
-static inline char *esirisc_cache_arch(struct esirisc_common *esirisc)
+static inline char *esirisc_cache_arch_name(struct esirisc_common *esirisc)
{
return esirisc->cache_arch == ESIRISC_CACHE_HARVARD ? "harvard" : "von_neumann";
}
diff --git a/src/target/esirisc_jtag.c b/src/target/esirisc_jtag.c
index 8ab47fa..333a622 100644
--- a/src/target/esirisc_jtag.c
+++ b/src/target/esirisc_jtag.c
@@ -265,6 +265,7 @@ int esirisc_jtag_read_byte(struct esirisc_jtag *jtag_info, uint32_t address, uin
return retval;
*data = *d;
+ LOG_DEBUG("address: 0x%" PRIx32 ", data: 0x%" PRIx8, address, *data);
return ERROR_OK;
}
@@ -292,6 +293,7 @@ int esirisc_jtag_read_hword(struct esirisc_jtag *jtag_info, uint32_t address, ui
return retval;
*data = le_to_h_u16(d);
+ LOG_DEBUG("address: 0x%" PRIx32 ", data: 0x%" PRIx16, address, *data);
return ERROR_OK;
}
@@ -319,6 +321,7 @@ int esirisc_jtag_read_word(struct esirisc_jtag *jtag_info, uint32_t address, uin
return retval;
*data = le_to_h_u32(d);
+ LOG_DEBUG("address: 0x%" PRIx32 ", data: 0x%" PRIx32, address, *data);
return ERROR_OK;
}
@@ -328,6 +331,8 @@ int esirisc_jtag_write_byte(struct esirisc_jtag *jtag_info, uint32_t address, ui
struct scan_field out_fields[2];
uint8_t a[4];
+ LOG_DEBUG("address: 0x%" PRIx32 ", data: 0x%" PRIx8, address, data);
+
out_fields[0].num_bits = 32;
out_fields[0].out_value = a;
h_u32_to_be(a, address);
@@ -346,6 +351,8 @@ int esirisc_jtag_write_hword(struct esirisc_jtag *jtag_info, uint32_t address, u
struct scan_field out_fields[2];
uint8_t a[4], d[2];
+ LOG_DEBUG("address: 0x%" PRIx32 ", data: 0x%" PRIx16, address, data);
+
out_fields[0].num_bits = 32;
out_fields[0].out_value = a;
h_u32_to_be(a, address);
@@ -365,6 +372,8 @@ int esirisc_jtag_write_word(struct esirisc_jtag *jtag_info, uint32_t address, ui
struct scan_field out_fields[2];
uint8_t a[4], d[4];
+ LOG_DEBUG("address: 0x%" PRIx32 ", data: 0x%" PRIx32, address, data);
+
out_fields[0].num_bits = 32;
out_fields[0].out_value = a;
h_u32_to_be(a, address);
@@ -400,6 +409,7 @@ int esirisc_jtag_read_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t
return retval;
*data = le_to_h_u32(d);
+ LOG_DEBUG("register: 0x%" PRIx32 ", data: 0x%" PRIx32, reg, *data);
return ERROR_OK;
}
@@ -409,6 +419,8 @@ int esirisc_jtag_write_reg(struct esirisc_jtag *jtag_info, uint8_t reg, uint32_t
struct scan_field out_fields[2];
uint8_t d[4];
+ LOG_DEBUG("register: 0x%" PRIx32 ", data: 0x%" PRIx32, reg, data);
+
out_fields[0].num_bits = 8;
out_fields[0].out_value = &reg;
out_fields[0].in_value = NULL;
@@ -445,6 +457,7 @@ int esirisc_jtag_read_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t
return retval;
*data = le_to_h_u32(d);
+ LOG_DEBUG("bank: 0x%" PRIx32 ", csr: 0x%" PRIx32 ", data: 0x%" PRIx32, bank, csr, *data);
return ERROR_OK;
}
@@ -454,6 +467,8 @@ int esirisc_jtag_write_csr(struct esirisc_jtag *jtag_info, uint8_t bank, uint8_t
struct scan_field out_fields[2];
uint8_t c[2], d[4];
+ LOG_DEBUG("bank: 0x%" PRIx32 ", csr: 0x%" PRIx32 ", data: 0x%" PRIx32, bank, csr, data);
+
out_fields[0].num_bits = 16;
out_fields[0].out_value = c;
h_u16_to_be(c, (csr << 5) | bank);
diff --git a/src/target/esirisc_jtag.h b/src/target/esirisc_jtag.h
index 8189ddc..5f8fe66 100644
--- a/src/target/esirisc_jtag.h
+++ b/src/target/esirisc_jtag.h
@@ -20,6 +20,7 @@
#ifndef OPENOCD_TARGET_ESIRISC_JTAG_H
#define OPENOCD_TARGET_ESIRISC_JTAG_H
+#include <helper/types.h>
#include <jtag/jtag.h>
/* TAP Instructions */
diff --git a/src/target/esirisc_trace.c b/src/target/esirisc_trace.c
new file mode 100644
index 0000000..4e0a155
--- /dev/null
+++ b/src/target/esirisc_trace.c
@@ -0,0 +1,1203 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Square, Inc. *
+ * Steven Stallion <stallion@squareup.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <helper/binarybuffer.h>
+#include <helper/command.h>
+#include <helper/fileio.h>
+#include <helper/log.h>
+#include <helper/types.h>
+#include <target/target.h>
+
+#include "esirisc.h"
+
+#define BIT_MASK(x) ((1 << (x)) - 1)
+
+/* Control Fields */
+#define CONTROL_ST (1<<0) /* Start */
+#define CONTROL_SP (1<<1) /* Stop */
+#define CONTROL_W (1<<2) /* Wrap */
+#define CONTROL_FC (1<<3) /* Flow Control */
+#define CONTROL_FMT(x) (((x) << 4) & 0x30) /* Format */
+#define CONTROL_PCB(x) (((x) << 10) & 0x7c00) /* PC Bits */
+
+/* Status Fields */
+#define STATUS_T (1<<0) /* Trace Started */
+#define STATUS_TD (1<<1) /* Trace Disabled */
+#define STATUS_W (1<<2) /* Wrapped */
+#define STATUS_O (1<<3) /* Overflow */
+
+/* Trigger Fields */
+#define TRIGGER_TST(x) (((x) << 0) & 0xf) /* Trigger Start */
+#define TRIGGER_DST (1<<7) /* Delay Start */
+#define TRIGGER_TSP(x) (((x) << 8) & 0xf00) /* Trigger Stop */
+#define TRIGGER_DSP (1<<15) /* Delay Start */
+
+static const char * const esirisc_trace_delay_strings[] = {
+ "none", "start", "stop", "both",
+};
+
+static const char * const esirisc_trace_format_strings[] = {
+ "full", "branch", "icache",
+};
+
+static const char * const esirisc_trace_id_strings[] = {
+ "sequential instruction",
+ "pipeline stall",
+ "direct branch",
+ "extended ID",
+};
+
+static const char * const esirisc_trace_ext_id_strings[] = {
+ "", /* unused */
+ "exception",
+ "eret",
+ "stop instruction",
+ "wait instruction",
+ "multicycle instruction",
+ "count",
+ "initial",
+ "indirect branch",
+ "end of trace",
+ "final",
+};
+
+static const char * const esirisc_trace_trigger_strings[] = {
+ "none", "pc", "load", "store", "exception", "eret", "wait", "stop",
+ "high", "low", /* start only */
+};
+
+static int esirisc_trace_clear_status(struct target *target)
+{
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+ int retval;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_STATUS, ~0);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: Status", target_name(target));
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_get_status(struct target *target, uint32_t *status)
+{
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ int retval = esirisc_jtag_read_csr(jtag_info, CSR_TRACE, CSR_TRACE_STATUS, status);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to read Trace CSR: Status", target_name(target));
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_start(struct target *target)
+{
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+ uint32_t control;
+ int retval;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ retval = esirisc_jtag_read_csr(jtag_info, CSR_TRACE, CSR_TRACE_CONTROL, &control);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to read Trace CSR: Control", target_name(target));
+ return retval;
+ }
+
+ control |= CONTROL_ST;
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_CONTROL, control);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: Control", target_name(target));
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_stop(struct target *target)
+{
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+ uint32_t control;
+ int retval;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ retval = esirisc_jtag_read_csr(jtag_info, CSR_TRACE, CSR_TRACE_CONTROL, &control);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to read Trace CSR: Control", target_name(target));
+ return retval;
+ }
+
+ control |= CONTROL_SP;
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_CONTROL, control);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: Control", target_name(target));
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_init(struct target *target)
+{
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ uint32_t control, trigger;
+ int retval;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ /* stop if running and clear status */
+ retval = esirisc_trace_stop(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = esirisc_trace_clear_status(target);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* initialize Control CSR */
+ control = CONTROL_FMT(trace_info->format)
+ | CONTROL_PCB(trace_info->pc_bits);
+
+ if (trace_info->buffer_wrap)
+ control |= CONTROL_W;
+
+ if (trace_info->flow_control)
+ control |= CONTROL_FC;
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_CONTROL, control);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: Control", target_name(target));
+ return retval;
+ }
+
+ /* initialize buffer CSRs */
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_BUFFER_START,
+ trace_info->buffer_start);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: BufferStart", target_name(target));
+ return retval;
+ }
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_BUFFER_END,
+ trace_info->buffer_end);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: BufferEnd", target_name(target));
+ return retval;
+ }
+
+ /*
+ * The BufferCurrent CSR must be initialized to the same value as
+ * BufferStart before tracing can be enabled:
+ */
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_BUFFER_CUR,
+ trace_info->buffer_start);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: BufferCurrent", target_name(target));
+ return retval;
+ }
+
+ /* initialize Trigger CSR */
+ trigger = TRIGGER_TST(trace_info->start_trigger)
+ | TRIGGER_TSP(trace_info->stop_trigger);
+
+ if (trace_info->delay == ESIRISC_TRACE_DELAY_START
+ || trace_info->delay == ESIRISC_TRACE_DELAY_BOTH) {
+ trigger |= TRIGGER_DST;
+ }
+
+ if (trace_info->delay == ESIRISC_TRACE_DELAY_STOP
+ || trace_info->delay == ESIRISC_TRACE_DELAY_BOTH) {
+ trigger |= TRIGGER_DSP;
+ }
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_TRIGGER, trigger);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: Trigger", target_name(target));
+ return retval;
+ }
+
+ /* initialize StartData/StartMask CSRs */
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_START_DATA,
+ trace_info->start_data);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: StartData", target_name(target));
+ return retval;
+ }
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_START_MASK,
+ trace_info->start_mask);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: StartMask", target_name(target));
+ return retval;
+ }
+
+ /* initialize StopData/StopMask CSRs */
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_STOP_DATA,
+ trace_info->stop_data);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: StopData", target_name(target));
+ return retval;
+ }
+
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_STOP_MASK,
+ trace_info->stop_mask);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: StopMask", target_name(target));
+ return retval;
+ }
+
+ /* initialize Delay CSR */
+ retval = esirisc_jtag_write_csr(jtag_info, CSR_TRACE, CSR_TRACE_DELAY,
+ trace_info->delay_cycles);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to write Trace CSR: Delay", target_name(target));
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_buf_get_u32(uint8_t *buffer, uint32_t size,
+ unsigned *pos, unsigned count, uint32_t *value)
+{
+ const unsigned num_bits = size * 8;
+
+ if (*pos+count > num_bits)
+ return ERROR_FAIL;
+
+ *value = buf_get_u32(buffer, *pos, count);
+ *pos += count;
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_buf_get_pc(struct target *target, uint8_t *buffer, uint32_t size,
+ unsigned *pos, uint32_t *value)
+{
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ int retval;
+
+ retval = esirisc_trace_buf_get_u32(buffer, size, pos, trace_info->pc_bits, value);
+ if (retval != ERROR_OK)
+ return retval;
+
+ *value <<= esirisc->num_bits - trace_info->pc_bits;
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_read_memory(struct target *target, target_addr_t address, uint32_t size,
+ uint8_t *buffer)
+{
+ int retval;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ retval = target_read_memory(target, address, 1, size, buffer);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to read trace data", target_name(target));
+ return retval;
+ }
+
+ return ERROR_OK;
+}
+
+static int esirisc_trace_read_buffer(struct target *target, uint8_t *buffer)
+{
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_jtag *jtag_info = &esirisc->jtag_info;
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ uint32_t buffer_cur, status;
+ int retval;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ retval = esirisc_jtag_read_csr(jtag_info, CSR_TRACE, CSR_TRACE_BUFFER_CUR, &buffer_cur);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("%s: failed to read Trace CSR: BufferCurrent", target_name(target));
+ return retval;
+ }
+
+ /*
+ * If the buffer has wrapped, the BufferCurrent CSR indicates the
+ * next address to be written (ie. the start address). These bytes
+ * must be dumped first to maintain coherency when analyzing
+ * captured data.
+ */
+ retval = esirisc_trace_get_status(target, &status);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (status & STATUS_W) {
+ uint32_t size = trace_info->buffer_end - buffer_cur;
+
+ retval = esirisc_trace_read_memory(target, buffer_cur, size, buffer);
+ if (retval != ERROR_OK)
+ return retval;
+
+ buffer += size;
+ }
+
+ return esirisc_trace_read_memory(target, trace_info->buffer_start,
+ buffer_cur - trace_info->buffer_start, buffer);
+}
+
+static int esirisc_trace_analyze_full(struct command_context *cmd_ctx, uint8_t *buffer, uint32_t size)
+{
+ struct target *target = get_current_target(cmd_ctx);
+ const uint32_t num_bits = size * 8;
+ int retval;
+
+ unsigned pos = 0;
+ while (pos < num_bits) {
+ uint32_t id;
+
+ retval = esirisc_trace_buf_get_u32(buffer, size, &pos, 2, &id);
+ if (retval != ERROR_OK)
+ goto fail;
+
+ switch (id) {
+ case ESIRISC_TRACE_ID_EXECUTE:
+ case ESIRISC_TRACE_ID_STALL:
+ case ESIRISC_TRACE_ID_BRANCH:
+ command_print(cmd_ctx, "%s", esirisc_trace_id_strings[id]);
+ break;
+
+ case ESIRISC_TRACE_ID_EXTENDED: {
+ uint32_t ext_id;
+
+ retval = esirisc_trace_buf_get_u32(buffer, size, &pos, 4, &ext_id);
+ if (retval != ERROR_OK)
+ goto fail;
+
+ switch (ext_id) {
+ case ESIRISC_TRACE_EXT_ID_STOP:
+ case ESIRISC_TRACE_EXT_ID_WAIT:
+ case ESIRISC_TRACE_EXT_ID_MULTICYCLE:
+ command_print(cmd_ctx, "%s", esirisc_trace_ext_id_strings[ext_id]);
+ break;
+
+ case ESIRISC_TRACE_EXT_ID_ERET:
+ case ESIRISC_TRACE_EXT_ID_PC:
+ case ESIRISC_TRACE_EXT_ID_INDIRECT:
+ case ESIRISC_TRACE_EXT_ID_END_PC: {
+ uint32_t pc;
+
+ retval = esirisc_trace_buf_get_pc(target, buffer, size, &pos, &pc);
+ if (retval != ERROR_OK)
+ goto fail;
+
+ command_print(cmd_ctx, "%s PC: 0x%" PRIx32,
+ esirisc_trace_ext_id_strings[ext_id], pc);
+
+ if (ext_id == ESIRISC_TRACE_EXT_ID_END_PC) {
+ command_print(cmd_ctx, "--- end of trace ---");
+ return ERROR_OK;
+ }
+ break;
+ }
+ case ESIRISC_TRACE_EXT_ID_EXCEPTION: {
+ uint32_t eid, epc;
+
+ retval = esirisc_trace_buf_get_u32(buffer, size, &pos, 6, &eid);
+ if (retval != ERROR_OK)
+ goto fail;
+
+ retval = esirisc_trace_buf_get_pc(target, buffer, size, &pos, &epc);
+ if (retval != ERROR_OK)
+ goto fail;
+
+ command_print(cmd_ctx, "%s EID: 0x%" PRIx32 ", EPC: 0x%" PRIx32,
+ esirisc_trace_ext_id_strings[ext_id], eid, epc);
+ break;
+ }
+ case ESIRISC_TRACE_EXT_ID_COUNT: {
+ uint32_t count;
+
+ retval = esirisc_trace_buf_get_u32(buffer, size, &pos, 6, &count);
+ if (retval != ERROR_OK)
+ goto fail;
+
+ command_print(cmd_ctx, "repeats %" PRId32 " %s", count,
+ (count == 1) ? "time" : "times");
+ break;
+ }
+ case ESIRISC_TRACE_EXT_ID_END:
+ command_print(cmd_ctx, "--- end of trace ---");
+ return ERROR_OK;
+
+ default:
+ command_print(cmd_ctx, "invalid extended trace ID: %" PRId32, ext_id);
+ return ERROR_FAIL;
+ }
+ break;
+ }
+ default:
+ command_print(cmd_ctx, "invalid trace ID: %" PRId32, id);
+ return ERROR_FAIL;
+ }
+ }
+
+fail:
+ command_print(cmd_ctx, "trace buffer too small");
+ return ERROR_BUF_TOO_SMALL;
+}
+
+static int esirisc_trace_analyze_simple(struct command_context *cmd_ctx, uint8_t *buffer, uint32_t size)
+{
+ struct target *target = get_current_target(cmd_ctx);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ const uint32_t end_of_trace = BIT_MASK(trace_info->pc_bits) << 1;
+ const uint32_t num_bits = size * 8;
+ int retval;
+
+ unsigned pos = 0;
+ while (pos < num_bits) {
+ uint32_t pc;
+
+ retval = esirisc_trace_buf_get_pc(target, buffer, size, &pos, &pc);
+ if (retval != ERROR_OK)
+ break;
+
+ if (pc == end_of_trace) {
+ command_print(cmd_ctx, "--- end of trace ---");
+ return ERROR_OK;
+ }
+
+ command_print(cmd_ctx, "PC: 0x%" PRIx32, pc);
+ }
+
+ command_print(cmd_ctx, "trace buffer too small");
+ return ERROR_BUF_TOO_SMALL;
+}
+
+static int esirisc_trace_analyze(struct command_context *cmd_ctx, uint8_t *buffer, uint32_t size)
+{
+ struct target *target = get_current_target(cmd_ctx);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+
+ switch (trace_info->format) {
+ case ESIRISC_TRACE_FORMAT_FULL:
+ command_print(cmd_ctx, "--- full pipeline ---");
+ return esirisc_trace_analyze_full(cmd_ctx, buffer, size);
+
+ case ESIRISC_TRACE_FORMAT_BRANCH:
+ command_print(cmd_ctx, "--- branches taken ---");
+ return esirisc_trace_analyze_full(cmd_ctx, buffer, size);
+
+ case ESIRISC_TRACE_FORMAT_ICACHE:
+ command_print(cmd_ctx, "--- icache misses ---");
+ return esirisc_trace_analyze_simple(cmd_ctx, buffer, size);
+
+ default:
+ command_print(cmd_ctx, "invalid trace format: %i", trace_info->format);
+ return ERROR_FAIL;
+ }
+}
+
+static int esirisc_trace_analyze_buffer(struct command_context *cmd_ctx)
+{
+ struct target *target = get_current_target(cmd_ctx);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ uint8_t *buffer;
+ uint32_t size;
+ int retval;
+
+ size = esirisc_trace_buffer_size(trace_info);
+ buffer = calloc(1, size);
+ if (buffer == NULL) {
+ command_print(cmd_ctx, "out of memory");
+ return ERROR_FAIL;
+ }
+
+ retval = esirisc_trace_read_buffer(target, buffer);
+ if (retval != ERROR_OK)
+ goto done;
+
+ retval = esirisc_trace_analyze(cmd_ctx, buffer, size);
+
+done:
+ free(buffer);
+
+ return retval;
+}
+
+static int esirisc_trace_analyze_memory(struct command_context *cmd_ctx,
+ target_addr_t address, uint32_t size)
+{
+ struct target *target = get_current_target(cmd_ctx);
+ uint8_t *buffer;
+ int retval;
+
+ buffer = calloc(1, size);
+ if (buffer == NULL) {
+ command_print(cmd_ctx, "out of memory");
+ return ERROR_FAIL;
+ }
+
+ retval = esirisc_trace_read_memory(target, address, size, buffer);
+ if (retval != ERROR_OK)
+ goto done;
+
+ retval = esirisc_trace_analyze(cmd_ctx, buffer, size);
+
+done:
+ free(buffer);
+
+ return retval;
+}
+
+static int esirisc_trace_dump(struct command_context *cmd_ctx, const char *filename,
+ uint8_t *buffer, uint32_t size)
+{
+ struct fileio *fileio;
+ size_t size_written;
+ int retval;
+
+ retval = fileio_open(&fileio, filename, FILEIO_WRITE, FILEIO_BINARY);
+ if (retval != ERROR_OK) {
+ command_print(cmd_ctx, "could not open dump file: %s", filename);
+ return retval;
+ }
+
+ retval = fileio_write(fileio, size, buffer, &size_written);
+ if (retval == ERROR_OK)
+ command_print(cmd_ctx, "trace data dumped to: %s", filename);
+ else
+ command_print(cmd_ctx, "could not write dump file: %s", filename);
+
+ fileio_close(fileio);
+
+ return retval;
+}
+
+static int esirisc_trace_dump_buffer(struct command_context *cmd_ctx, const char *filename)
+{
+ struct target *target = get_current_target(cmd_ctx);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ uint8_t *buffer;
+ uint32_t size;
+ int retval;
+
+ size = esirisc_trace_buffer_size(trace_info);
+ buffer = calloc(1, size);
+ if (buffer == NULL) {
+ command_print(cmd_ctx, "out of memory");
+ return ERROR_FAIL;
+ }
+
+ retval = esirisc_trace_read_buffer(target, buffer);
+ if (retval != ERROR_OK)
+ goto done;
+
+ retval = esirisc_trace_dump(cmd_ctx, filename, buffer, size);
+
+done:
+ free(buffer);
+
+ return retval;
+}
+
+static int esirisc_trace_dump_memory(struct command_context *cmd_ctx, const char *filename,
+ target_addr_t address, uint32_t size)
+{
+ struct target *target = get_current_target(cmd_ctx);
+ uint8_t *buffer;
+ int retval;
+
+ buffer = calloc(1, size);
+ if (buffer == NULL) {
+ command_print(cmd_ctx, "out of memory");
+ return ERROR_FAIL;
+ }
+
+ retval = esirisc_trace_read_memory(target, address, size, buffer);
+ if (retval != ERROR_OK)
+ goto done;
+
+ retval = esirisc_trace_dump(cmd_ctx, filename, buffer, size);
+
+done:
+ free(buffer);
+
+ return retval;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_init_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+
+ if (!esirisc->has_trace) {
+ command_print(CMD_CTX, "target does not support trace");
+ return ERROR_FAIL;
+ }
+
+ int retval = esirisc_trace_init(target);
+ if (retval == ERROR_OK)
+ command_print(CMD_CTX, "trace initialized");
+
+ return retval;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_info_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+
+ if (!esirisc->has_trace) {
+ command_print(CMD_CTX, "target does not support trace");
+ return ERROR_FAIL;
+ }
+
+ if (esirisc_trace_is_fifo(trace_info))
+ command_print(CMD_CTX, "trace FIFO address: 0x%" TARGET_PRIxADDR,
+ trace_info->buffer_start);
+ else {
+ command_print(CMD_CTX, "trace buffer start: 0x%" TARGET_PRIxADDR,
+ trace_info->buffer_start);
+ command_print(CMD_CTX, "trace buffer end: 0x%" TARGET_PRIxADDR,
+ trace_info->buffer_end);
+ command_print(CMD_CTX, "trace buffer will %swrap",
+ trace_info->buffer_wrap ? "" : "not ");
+ }
+
+ command_print(CMD_CTX, "flow control: %s",
+ trace_info->flow_control ? "enabled" : "disabled");
+
+ command_print(CMD_CTX, "trace format: %s",
+ esirisc_trace_format_strings[trace_info->format]);
+ command_print(CMD_CTX, "number of PC bits: %i", trace_info->pc_bits);
+
+ command_print(CMD_CTX, "start trigger: %s",
+ esirisc_trace_trigger_strings[trace_info->start_trigger]);
+ command_print(CMD_CTX, "start data: 0x%" PRIx32, trace_info->start_data);
+ command_print(CMD_CTX, "start mask: 0x%" PRIx32, trace_info->start_mask);
+
+ command_print(CMD_CTX, "stop trigger: %s",
+ esirisc_trace_trigger_strings[trace_info->stop_trigger]);
+ command_print(CMD_CTX, "stop data: 0x%" PRIx32, trace_info->stop_data);
+ command_print(CMD_CTX, "stop mask: 0x%" PRIx32, trace_info->stop_mask);
+
+ command_print(CMD_CTX, "trigger delay: %s",
+ esirisc_trace_delay_strings[trace_info->delay]);
+ command_print(CMD_CTX, "trigger delay cycles: %i", trace_info->delay_cycles);
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_status_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ uint32_t status;
+
+ if (!esirisc->has_trace) {
+ command_print(CMD_CTX, "target does not support trace");
+ return ERROR_FAIL;
+ }
+
+ int retval = esirisc_trace_get_status(target, &status);
+ if (retval != ERROR_OK)
+ return retval;
+
+ command_print(CMD_CTX, "trace is %s%s%s%s",
+ (status & STATUS_T) ? "started" : "stopped",
+ (status & STATUS_TD) ? ", disabled" : "",
+ (status & STATUS_W) ? ", wrapped" : "",
+ (status & STATUS_O) ? ", overflowed" : "");
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_start_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+
+ if (!esirisc->has_trace) {
+ command_print(CMD_CTX, "target does not support trace");
+ return ERROR_FAIL;
+ }
+
+ int retval = esirisc_trace_start(target);
+ if (retval == ERROR_OK)
+ command_print(CMD_CTX, "trace started");
+
+ return retval;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_stop_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+
+ if (!esirisc->has_trace) {
+ command_print(CMD_CTX, "target does not support trace");
+ return ERROR_FAIL;
+ }
+
+ int retval = esirisc_trace_stop(target);
+ if (retval == ERROR_OK)
+ command_print(CMD_CTX, "trace stopped");
+
+ return retval;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_analyze_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ target_addr_t address;
+ uint32_t size;
+
+ if (!esirisc->has_trace) {
+ command_print(CMD_CTX, "target does not support trace");
+ return ERROR_FAIL;
+ }
+
+ if (CMD_ARGC != 0 && CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 0) {
+ /*
+ * Use of the Trace FIFO typically involves DMA to a peripheral
+ * (eg. SPI) or a separately managed buffer in memory, neither
+ * of which may be under our control. If the destination address
+ * and size are known in the latter case, they may be specified
+ * as arguments as a workaround.
+ */
+ if (esirisc_trace_is_fifo(trace_info)) {
+ command_print(CMD_CTX, "analyze from FIFO not supported");
+ return ERROR_FAIL;
+ }
+
+ return esirisc_trace_analyze_buffer(CMD_CTX);
+ } else {
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[0], address);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+
+ return esirisc_trace_analyze_memory(CMD_CTX, address, size);
+ }
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_dump_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ target_addr_t address;
+ uint32_t size;
+
+ if (!esirisc->has_trace) {
+ command_print(CMD_CTX, "target does not support trace");
+ return ERROR_FAIL;
+ }
+
+ if (CMD_ARGC != 1 && CMD_ARGC != 3)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1) {
+ /* also see: handle_esirisc_trace_analyze_command() */
+ if (esirisc_trace_is_fifo(trace_info)) {
+ command_print(CMD_CTX, "dump from FIFO not supported");
+ return ERROR_FAIL;
+ }
+
+ return esirisc_trace_dump_buffer(CMD_CTX, CMD_ARGV[0]);
+ } else {
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[0], address);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+
+ return esirisc_trace_dump_memory(CMD_CTX, CMD_ARGV[2], address, size);
+ }
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_buffer_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ uint32_t size;
+
+ if (CMD_ARGC < 2 || CMD_ARGC > 3)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[0], trace_info->buffer_start);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
+
+ trace_info->buffer_end = trace_info->buffer_start + size;
+
+ if (CMD_ARGC == 3) {
+ if (strcmp("wrap", CMD_ARGV[2]) == 0)
+ trace_info->buffer_wrap = true;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_fifo_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[0], trace_info->buffer_start);
+
+ /* FIFOs have the same start and end address */
+ trace_info->buffer_end = trace_info->buffer_start;
+ trace_info->buffer_wrap = true;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_flow_control_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (strcmp(CMD_ARGV[0], "enable") == 0)
+ trace_info->flow_control = true;
+ else if (strcmp(CMD_ARGV[0], "disable") == 0)
+ trace_info->flow_control = false;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_format_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+ int pc_bits;
+
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (strcmp(CMD_ARGV[0], "full") == 0)
+ trace_info->format = ESIRISC_TRACE_FORMAT_FULL;
+ else if (strcmp(CMD_ARGV[0], "branch") == 0)
+ trace_info->format = ESIRISC_TRACE_FORMAT_BRANCH;
+ else if (strcmp(CMD_ARGV[0], "icache") == 0)
+ trace_info->format = ESIRISC_TRACE_FORMAT_ICACHE;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], pc_bits);
+
+ if (pc_bits < 1 || pc_bits > 31) {
+ command_print(CMD_CTX, "invalid pc_bits: %i; must be 1..31", pc_bits);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ trace_info->pc_bits = pc_bits;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_trigger_start_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+
+ if (CMD_ARGC != 1 && CMD_ARGC != 3)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (strcmp(CMD_ARGV[0], "none") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_NONE;
+ else if (strcmp(CMD_ARGV[0], "pc") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_PC;
+ else if (strcmp(CMD_ARGV[0], "load") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_LOAD;
+ else if (strcmp(CMD_ARGV[0], "store") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_STORE;
+ else if (strcmp(CMD_ARGV[0], "exception") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_EXCEPTION;
+ else if (strcmp(CMD_ARGV[0], "eret") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_ERET;
+ else if (strcmp(CMD_ARGV[0], "wait") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_WAIT;
+ else if (strcmp(CMD_ARGV[0], "stop") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_STOP;
+ else if (strcmp(CMD_ARGV[0], "high") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_HIGH;
+ else if (strcmp(CMD_ARGV[0], "low") == 0)
+ trace_info->start_trigger = ESIRISC_TRACE_TRIGGER_LOW;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 3) {
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], trace_info->start_data);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], trace_info->start_mask);
+ } else {
+ trace_info->start_data = 0;
+ trace_info->start_mask = 0;
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_trigger_stop_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+
+ if (CMD_ARGC != 1 && CMD_ARGC != 3)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (strcmp(CMD_ARGV[0], "none") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_NONE;
+ else if (strcmp(CMD_ARGV[0], "pc") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_PC;
+ else if (strcmp(CMD_ARGV[0], "load") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_LOAD;
+ else if (strcmp(CMD_ARGV[0], "store") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_STORE;
+ else if (strcmp(CMD_ARGV[0], "exception") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_EXCEPTION;
+ else if (strcmp(CMD_ARGV[0], "eret") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_ERET;
+ else if (strcmp(CMD_ARGV[0], "wait") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_WAIT;
+ else if (strcmp(CMD_ARGV[0], "stop") == 0)
+ trace_info->stop_trigger = ESIRISC_TRACE_TRIGGER_STOP;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 3) {
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], trace_info->stop_data);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], trace_info->stop_mask);
+ } else {
+ trace_info->stop_data = 0;
+ trace_info->stop_mask = 0;
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_esirisc_trace_trigger_delay_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ struct esirisc_common *esirisc = target_to_esirisc(target);
+ struct esirisc_trace *trace_info = &esirisc->trace_info;
+
+ if (CMD_ARGC < 1 || CMD_ARGC > 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (strcmp(CMD_ARGV[0], "none") == 0)
+ trace_info->delay = ESIRISC_TRACE_DELAY_NONE;
+ else if (strcmp(CMD_ARGV[0], "start") == 0)
+ trace_info->delay = ESIRISC_TRACE_DELAY_START;
+ else if (strcmp(CMD_ARGV[0], "stop") == 0)
+ trace_info->delay = ESIRISC_TRACE_DELAY_STOP;
+ else if (strcmp(CMD_ARGV[0], "both") == 0)
+ trace_info->delay = ESIRISC_TRACE_DELAY_BOTH;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (trace_info->delay == ESIRISC_TRACE_DELAY_NONE)
+ trace_info->delay_cycles = 0;
+ else {
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], trace_info->delay_cycles);
+ }
+
+ return ERROR_OK;
+}
+
+static const struct command_registration esirisc_trace_exec_command_handlers[] = {
+ {
+ .name = "init",
+ .handler = handle_esirisc_trace_init_command,
+ .mode = COMMAND_EXEC,
+ .help = "initialize trace collection",
+ .usage = "",
+ },
+ {
+ .name = "info",
+ .handler = handle_esirisc_trace_info_command,
+ .mode = COMMAND_EXEC,
+ .help = "display trace configuration",
+ .usage = "",
+ },
+ {
+ .name = "status",
+ .handler = handle_esirisc_trace_status_command,
+ .mode = COMMAND_EXEC,
+ .help = "display trace collection status",
+ .usage = "",
+ },
+ {
+ .name = "start",
+ .handler = handle_esirisc_trace_start_command,
+ .mode = COMMAND_EXEC,
+ .help = "start trace collection",
+ .usage = "",
+ },
+ {
+ .name = "stop",
+ .handler = handle_esirisc_trace_stop_command,
+ .mode = COMMAND_EXEC,
+ .help = "stop trace collection",
+ .usage = "",
+ },
+ {
+ .name = "analyze",
+ .handler = handle_esirisc_trace_analyze_command,
+ .mode = COMMAND_EXEC,
+ .usage = "[address size]",
+ .help = "analyze collected trace data",
+ },
+ {
+ .name = "dump",
+ .handler = handle_esirisc_trace_dump_command,
+ .mode = COMMAND_EXEC,
+ .help = "dump collected trace data to file",
+ .usage = "[address size] filename",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration esirisc_trace_trigger_any_command_handlers[] = {
+ {
+ .name = "start",
+ .handler = handle_esirisc_trace_trigger_start_command,
+ .mode = COMMAND_ANY,
+ .help = "configure trigger start condition",
+ .usage = "('none'|'pc'|'load'|'store'|'exception'|'eret'|'wait'|'stop'|'high'|'low')"
+ " [start_data start_mask]",
+ },
+ {
+ .name = "stop",
+ .handler = handle_esirisc_trace_trigger_stop_command,
+ .mode = COMMAND_ANY,
+ .help = "configure trigger stop condition",
+ .usage = "('none'|'pc'|'load'|'store'|'exception'|'eret'|'wait'|'stop')"
+ " [stop_data stop_mask]",
+ },
+ {
+ .name = "delay",
+ .handler = handle_esirisc_trace_trigger_delay_command,
+ .mode = COMMAND_ANY,
+ .help = "configure trigger start/stop delay in clock cycles",
+ .usage = "('none'|'start'|'stop'|'both') [cycles]",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration esirisc_trace_any_command_handlers[] = {
+ {
+ .name = "buffer",
+ .handler = handle_esirisc_trace_buffer_command,
+ .mode = COMMAND_ANY,
+ .help = "configure trace buffer",
+ .usage = "address size ['wrap']",
+ },
+ {
+ .name = "fifo",
+ .handler = handle_esirisc_trace_fifo_command,
+ .mode = COMMAND_ANY,
+ .help = "configure trace FIFO",
+ .usage = "address",
+ },
+ {
+ .name = "flow_control",
+ .handler = handle_esirisc_trace_flow_control_command,
+ .mode = COMMAND_ANY,
+ .help = "enable or disable stalling CPU to collect trace data",
+ .usage = "('enable'|'disable')",
+ },
+ {
+ .name = "format",
+ .handler = handle_esirisc_trace_format_command,
+ .mode = COMMAND_ANY,
+ .help = "configure trace format",
+ .usage = "('full'|'branch'|'icache') pc_bits",
+ },
+ {
+ .name = "trigger",
+ .mode = COMMAND_ANY,
+ .help = "eSi-Trace trigger command group",
+ .usage = "",
+ .chain = esirisc_trace_trigger_any_command_handlers,
+ },
+ {
+ .chain = esirisc_trace_exec_command_handlers
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+const struct command_registration esirisc_trace_command_handlers[] = {
+ {
+ .name = "trace",
+ .mode = COMMAND_ANY,
+ .help = "eSi-Trace command group",
+ .usage = "",
+ .chain = esirisc_trace_any_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
diff --git a/src/target/esirisc_trace.h b/src/target/esirisc_trace.h
new file mode 100644
index 0000000..c3cc6e9
--- /dev/null
+++ b/src/target/esirisc_trace.h
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * Copyright (C) 2018 by Square, Inc. *
+ * Steven Stallion <stallion@squareup.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESIRISC_TRACE_H
+#define OPENOCD_TARGET_ESIRISC_TRACE_H
+
+#include <helper/command.h>
+#include <helper/types.h>
+#include <target/target.h>
+
+enum esirisc_trace_delay {
+ ESIRISC_TRACE_DELAY_NONE,
+ ESIRISC_TRACE_DELAY_START,
+ ESIRISC_TRACE_DELAY_STOP,
+ ESIRISC_TRACE_DELAY_BOTH,
+};
+
+enum esirisc_trace_format {
+ ESIRISC_TRACE_FORMAT_FULL,
+ ESIRISC_TRACE_FORMAT_BRANCH,
+ ESIRISC_TRACE_FORMAT_ICACHE,
+};
+
+enum esirisc_trace_id {
+ ESIRISC_TRACE_ID_EXECUTE,
+ ESIRISC_TRACE_ID_STALL,
+ ESIRISC_TRACE_ID_BRANCH,
+ ESIRISC_TRACE_ID_EXTENDED,
+};
+
+enum esirisc_trace_ext_id {
+ ESIRISC_TRACE_EXT_ID_EXCEPTION = 1,
+ ESIRISC_TRACE_EXT_ID_ERET,
+ ESIRISC_TRACE_EXT_ID_STOP,
+ ESIRISC_TRACE_EXT_ID_WAIT,
+ ESIRISC_TRACE_EXT_ID_MULTICYCLE,
+ ESIRISC_TRACE_EXT_ID_COUNT,
+ ESIRISC_TRACE_EXT_ID_PC,
+ ESIRISC_TRACE_EXT_ID_INDIRECT,
+ ESIRISC_TRACE_EXT_ID_END,
+ ESIRISC_TRACE_EXT_ID_END_PC,
+};
+
+enum esirisc_trace_trigger {
+ ESIRISC_TRACE_TRIGGER_NONE,
+ ESIRISC_TRACE_TRIGGER_PC,
+ ESIRISC_TRACE_TRIGGER_LOAD,
+ ESIRISC_TRACE_TRIGGER_STORE,
+ ESIRISC_TRACE_TRIGGER_EXCEPTION,
+ ESIRISC_TRACE_TRIGGER_ERET,
+ ESIRISC_TRACE_TRIGGER_WAIT,
+ ESIRISC_TRACE_TRIGGER_STOP,
+ ESIRISC_TRACE_TRIGGER_HIGH,
+ ESIRISC_TRACE_TRIGGER_LOW,
+};
+
+struct esirisc_trace {
+ target_addr_t buffer_start;
+ target_addr_t buffer_end;
+ bool buffer_wrap;
+ bool flow_control;
+
+ enum esirisc_trace_format format;
+ int pc_bits;
+
+ enum esirisc_trace_trigger start_trigger;
+ uint32_t start_data;
+ uint32_t start_mask;
+
+ enum esirisc_trace_trigger stop_trigger;
+ uint32_t stop_data;
+ uint32_t stop_mask;
+
+ enum esirisc_trace_delay delay;
+ uint32_t delay_cycles;
+};
+
+extern const struct command_registration esirisc_trace_command_handlers[];
+
+static inline uint32_t esirisc_trace_buffer_size(struct esirisc_trace *trace_info)
+{
+ return trace_info->buffer_end - trace_info->buffer_start;
+}
+
+static inline bool esirisc_trace_is_fifo(struct esirisc_trace *trace_info)
+{
+ return trace_info->buffer_start == trace_info->buffer_end;
+}
+
+#endif /* OPENOCD_TARGET_ESIRISC_TRACE_H */