diff options
-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 */ |