diff options
author | Steven Stallion <stallion@squareup.com> | 2018-10-24 21:29:03 -0500 |
---|---|---|
committer | Matthias Welwarsky <matthias@welwarsky.de> | 2018-12-12 08:47:44 +0000 |
commit | c5eb99082535574167294443be1e60077e3f6246 (patch) | |
tree | 4c2421624ae987ffe38321e9360bb41582953f6c | |
parent | 7ae6b04b982428e6ae3d6daa6f5e77482a460c40 (diff) | |
download | riscv-openocd-c5eb99082535574167294443be1e60077e3f6246.zip riscv-openocd-c5eb99082535574167294443be1e60077e3f6246.tar.gz riscv-openocd-c5eb99082535574167294443be1e60077e3f6246.tar.bz2 |
esirisc: support eSi-Trace
This patch adds support for instruction tracing to eSi-RISC targets. The
command interface is borrowed heavily from ETM; eSi-Trace uses a less
sophisticated model for tracing, however the setup and usage is similar.
This patch also cleans up the command interfaces of the other esirisc
command groups and adds additional debugging information to log messages
when dealing with CSRs.
This patch "finalizes" support for 32-bit eSi-RISC targets.
Change-Id: Ia2a9de79a3c7c066240b5212721fb1b7584a9a45
Signed-off-by: Steven Stallion <stallion@squareup.com>
Reviewed-on: http://openocd.zylin.com/4780
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
-rw-r--r-- | doc/openocd.texi | 179 | ||||
-rw-r--r-- | src/flash/nor/esirisc_flash.c | 29 | ||||
-rw-r--r-- | src/target/Makefile.am | 6 | ||||
-rw-r--r-- | src/target/esirisc.c | 192 | ||||
-rw-r--r-- | src/target/esirisc.h | 12 | ||||
-rw-r--r-- | src/target/esirisc_jtag.c | 15 | ||||
-rw-r--r-- | src/target/esirisc_jtag.h | 1 | ||||
-rw-r--r-- | src/target/esirisc_trace.c | 1203 | ||||
-rw-r--r-- | src/target/esirisc_trace.h | 105 |
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 = ® 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 */ |