target/espressif: add application tracing functionality over JTAG
This feature allows to transfer arbitrary data between host and ESP32 via JTAG. The main use cases: 1- Collecting application specific data 2- Lightweight logging to the host 3- System behaviour analysis with SEGGER SystemView 4- Source code coverage Signed-off-by: Erhan Kurubas <erhan.kurubas@espressif.com> Change-Id: I95dee00ac22891fa326915a3fcac3c088cbb2afc Reviewed-on: https://review.openocd.org/c/openocd/+/7163 Tested-by: jenkins Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
This commit is contained in:
parent
0384fe5d59
commit
8d1dcf293a
|
@ -11137,6 +11137,46 @@ Stop current trace as started by the tracestart command.
|
|||
Dump trace memory to a file.
|
||||
@end deffn
|
||||
|
||||
@section Espressif Specific Commands
|
||||
|
||||
@deffn {Command} {esp apptrace} (start <destination> [<poll_period> [<trace_size> [<stop_tmo> [<wait4halt> [<skip_size>]]]]])
|
||||
Starts
|
||||
@uref{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#application-level-tracing-library, application level tracing}.
|
||||
Data will be stored to specified destination. Available destinations are:
|
||||
@itemize @bullet
|
||||
@item @code{file://<outfile>} - Save trace logs into file.
|
||||
@item @code{tcp://<host>:<port>} - Send trace logs to tcp port on specified host. OpenOCD will act as a tcp client.
|
||||
@item @code{con:} - Print trace logs to the stdout.
|
||||
@end itemize
|
||||
Other parameters will be same for each destination.
|
||||
@itemize @bullet
|
||||
@item @code{poll_period} - trace data polling period in ms.
|
||||
@item @code{trace_size} - maximum trace data size.
|
||||
Tracing will be stopped automatically when that amount is reached.
|
||||
Use "-1" to disable the limitation.
|
||||
@item @code{stop_tmo} - Data reception timeout in ms.
|
||||
Tracing will be stopped automatically when no data is received within that period.
|
||||
@item @code{wait4halt} - if non-zero then wait for target to be halted before tracing start.
|
||||
@item @code{skip_size} - amount of tracing data to be skipped before writing it to destination.
|
||||
@end itemize
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {esp apptrace} (stop)
|
||||
Stops tracing started with above command.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {esp apptrace} (status)
|
||||
Requests ongoing tracing status.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {esp apptrace} (dump file://<outfile>)
|
||||
Dumps tracing data from target buffer. It can be useful to dump the latest data
|
||||
buffered on target for post-mortem analysis. For example when target starts tracing automatically
|
||||
w/o OpenOCD command and keeps only the latest data window which fit into the buffer.
|
||||
@uref{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#application-level-tracing-library, application level tracing}.
|
||||
Data will be stored to specified destination.
|
||||
@end deffn
|
||||
|
||||
@anchor{softwaredebugmessagesandtracing}
|
||||
@section Software Debug Messages and Tracing
|
||||
@cindex Linux-ARM DCC support
|
||||
|
|
|
@ -8,6 +8,10 @@ noinst_LTLIBRARIES += %D%/libespressif.la
|
|||
%D%/esp_xtensa_smp.h \
|
||||
%D%/esp_xtensa_semihosting.c \
|
||||
%D%/esp_xtensa_semihosting.h \
|
||||
%D%/esp_xtensa_apptrace.c \
|
||||
%D%/esp_xtensa_apptrace.h \
|
||||
%D%/esp32_apptrace.c \
|
||||
%D%/esp32_apptrace.h \
|
||||
%D%/esp32.c \
|
||||
%D%/esp32s2.c \
|
||||
%D%/esp32s3.c \
|
||||
|
|
|
@ -440,6 +440,11 @@ static const struct command_registration esp32_command_handlers[] = {
|
|||
{
|
||||
.chain = esp_xtensa_smp_command_handlers,
|
||||
},
|
||||
{
|
||||
.name = "esp",
|
||||
.usage = "",
|
||||
.chain = esp32_apptrace_command_handlers,
|
||||
},
|
||||
{
|
||||
.name = "esp32",
|
||||
.usage = "",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,126 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/***************************************************************************
|
||||
* ESP32 application trace module *
|
||||
* Copyright (C) 2017-2019 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_TARGET_ESP32_APPTRACE_H
|
||||
#define OPENOCD_TARGET_ESP32_APPTRACE_H
|
||||
|
||||
#include <helper/command.h>
|
||||
#include <helper/time_support.h>
|
||||
#include <target/target.h>
|
||||
|
||||
#define ESP32_APPTRACE_MAX_CORES_NUM 2
|
||||
|
||||
struct esp32_apptrace_hw {
|
||||
uint32_t max_block_id;
|
||||
uint32_t (*max_block_size_get)(struct target *target);
|
||||
int (*status_reg_read)(struct target *target, uint32_t *stat);
|
||||
int (*ctrl_reg_write)(struct target *target,
|
||||
uint32_t block_id,
|
||||
uint32_t len,
|
||||
bool conn,
|
||||
bool data);
|
||||
int (*ctrl_reg_read)(struct target *target,
|
||||
uint32_t *block_id,
|
||||
uint32_t *len,
|
||||
bool *conn);
|
||||
int (*data_len_read)(struct target *target,
|
||||
uint32_t *block_id,
|
||||
uint32_t *len);
|
||||
int (*data_read)(struct target *target,
|
||||
uint32_t size,
|
||||
uint8_t *buffer,
|
||||
uint32_t block_id,
|
||||
bool ack);
|
||||
uint32_t (*usr_block_max_size_get)(struct target *target);
|
||||
int (*buffs_write)(struct target *target,
|
||||
uint32_t bufs_num,
|
||||
uint32_t buf_sz[],
|
||||
const uint8_t *bufs[],
|
||||
uint32_t block_id,
|
||||
bool ack,
|
||||
bool data);
|
||||
int (*leave_trace_crit_section_start)(struct target *target);
|
||||
int (*leave_trace_crit_section_stop)(struct target *target);
|
||||
};
|
||||
|
||||
struct esp_apptrace_host2target_hdr {
|
||||
uint16_t block_sz;
|
||||
};
|
||||
|
||||
struct esp32_apptrace_dest {
|
||||
void *priv;
|
||||
int (*write)(void *priv, uint8_t *data, int size);
|
||||
int (*clean)(void *priv);
|
||||
bool log_progress;
|
||||
};
|
||||
|
||||
struct esp32_apptrace_format {
|
||||
uint32_t hdr_sz;
|
||||
int (*core_id_get)(struct target *target, uint8_t *hdr_buf);
|
||||
uint32_t (*usr_block_len_get)(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len);
|
||||
};
|
||||
|
||||
struct esp32_apptrace_cmd_stats {
|
||||
uint32_t incompl_blocks;
|
||||
uint32_t lost_bytes;
|
||||
float min_blk_read_time;
|
||||
float max_blk_read_time;
|
||||
float min_blk_proc_time;
|
||||
float max_blk_proc_time;
|
||||
};
|
||||
|
||||
struct esp32_apptrace_cmd_ctx {
|
||||
volatile int running;
|
||||
int mode;
|
||||
/* TODO: use subtargets from target arch info */
|
||||
struct target *cpus[ESP32_APPTRACE_MAX_CORES_NUM];
|
||||
/* TODO: use cores num from target */
|
||||
unsigned int cores_num;
|
||||
const struct esp32_apptrace_hw *hw;
|
||||
enum target_state target_state;
|
||||
uint32_t last_blk_id;
|
||||
struct list_head free_trace_blocks;
|
||||
struct list_head ready_trace_blocks;
|
||||
uint32_t max_trace_block_sz;
|
||||
struct esp32_apptrace_format trace_format;
|
||||
int (*process_data)(struct esp32_apptrace_cmd_ctx *ctx, unsigned int core_id, uint8_t *data, uint32_t data_len);
|
||||
void (*auto_clean)(struct esp32_apptrace_cmd_ctx *ctx);
|
||||
uint32_t tot_len;
|
||||
uint32_t raw_tot_len;
|
||||
float stop_tmo;
|
||||
struct esp32_apptrace_cmd_stats stats;
|
||||
struct duration read_time;
|
||||
struct duration idle_time;
|
||||
void *cmd_priv;
|
||||
struct target *target;
|
||||
struct command_invocation *cmd;
|
||||
};
|
||||
|
||||
struct esp32_apptrace_cmd_data {
|
||||
struct esp32_apptrace_dest data_dest;
|
||||
uint32_t poll_period;
|
||||
uint32_t max_len;
|
||||
uint32_t skip_len;
|
||||
bool wait4halt;
|
||||
};
|
||||
|
||||
int esp32_apptrace_cmd_ctx_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, struct command_invocation *cmd, int mode);
|
||||
int esp32_apptrace_cmd_ctx_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx);
|
||||
void esp32_apptrace_cmd_args_parse(struct esp32_apptrace_cmd_ctx *cmd_ctx,
|
||||
struct esp32_apptrace_cmd_data *cmd_data,
|
||||
const char **argv,
|
||||
int argc);
|
||||
int esp32_apptrace_dest_init(struct esp32_apptrace_dest dest[], const char *dest_paths[], unsigned int max_dests);
|
||||
int esp32_apptrace_dest_cleanup(struct esp32_apptrace_dest dest[], unsigned int max_dests);
|
||||
int esp_apptrace_usr_block_write(const struct esp32_apptrace_hw *hw, struct target *target,
|
||||
uint32_t block_id,
|
||||
const uint8_t *data,
|
||||
uint32_t size);
|
||||
|
||||
extern const struct command_registration esp32_apptrace_command_handlers[];
|
||||
|
||||
#endif /* OPENOCD_TARGET_ESP32_APPTRACE_H */
|
|
@ -496,6 +496,11 @@ static const struct command_registration esp32s2_command_handlers[] = {
|
|||
{
|
||||
.chain = xtensa_command_handlers,
|
||||
},
|
||||
{
|
||||
.name = "esp",
|
||||
.usage = "",
|
||||
.chain = esp32_apptrace_command_handlers,
|
||||
},
|
||||
{
|
||||
.name = "arm",
|
||||
.mode = COMMAND_ANY,
|
||||
|
|
|
@ -364,6 +364,11 @@ static const struct command_registration esp32s3_command_handlers[] = {
|
|||
.usage = "",
|
||||
.chain = esp_xtensa_smp_command_handlers,
|
||||
},
|
||||
{
|
||||
.name = "esp",
|
||||
.usage = "",
|
||||
.chain = esp32_apptrace_command_handlers,
|
||||
},
|
||||
{
|
||||
.name = "esp32",
|
||||
.usage = "",
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <target/smp.h>
|
||||
#include "esp_xtensa_apptrace.h"
|
||||
#include <target/register.h>
|
||||
#include "esp_xtensa.h"
|
||||
#include "esp_semihosting.h"
|
||||
|
@ -25,6 +26,7 @@ int esp_xtensa_init_arch_info(struct target *target,
|
|||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
esp_xtensa->semihost.ops = (struct esp_semihost_ops *)semihost_ops;
|
||||
esp_xtensa->apptrace.hw = &esp_xtensa_apptrace_hw;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,10 +12,12 @@
|
|||
#include <target/xtensa/xtensa.h>
|
||||
#include "esp_xtensa.h"
|
||||
#include "esp_semihosting.h"
|
||||
#include "esp_xtensa_apptrace.h"
|
||||
|
||||
struct esp_xtensa_common {
|
||||
struct xtensa xtensa; /* must be the first element */
|
||||
struct esp_semihost_data semihost;
|
||||
struct esp_xtensa_apptrace_info apptrace;
|
||||
};
|
||||
|
||||
static inline struct esp_xtensa_common *target_to_esp_xtensa(struct target *target)
|
||||
|
|
|
@ -0,0 +1,497 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Xtensa application tracing module for OpenOCD *
|
||||
* Copyright (C) 2017 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
How it works?
|
||||
https://github.com/espressif/esp-idf/blob/master/components/app_trace/port/xtensa/port.c#L8
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <helper/align.h>
|
||||
#include <target/xtensa/xtensa.h>
|
||||
#include <target/xtensa/xtensa_debug_module.h>
|
||||
#include "esp_xtensa_apptrace.h"
|
||||
|
||||
/* TRAX is disabled, so we use its registers for our own purposes
|
||||
* | 31..XXXXXX..24 | 23 .(host_connect). 23 | 22 .(host_data). 22| 21..(block_id)..15 | 14..(block_len)..0 |
|
||||
*/
|
||||
#define XTENSA_APPTRACE_CTRL_REG XDMREG_DELAYCNT
|
||||
#define XTENSA_APPTRACE_BLOCK_ID_MSK 0x7FUL
|
||||
#define XTENSA_APPTRACE_BLOCK_ID_MAX XTENSA_APPTRACE_BLOCK_ID_MSK
|
||||
/* if non-zero then apptrace code entered the critical section and the value is an address of the
|
||||
* critical section's exit point */
|
||||
#define XTENSA_APPTRACE_STAT_REG XDMREG_TRIGGERPC
|
||||
|
||||
#define XTENSA_APPTRACE_BLOCK_LEN_MSK 0x7FFFUL
|
||||
#define XTENSA_APPTRACE_BLOCK_LEN(_l_) ((_l_) & XTENSA_APPTRACE_BLOCK_LEN_MSK)
|
||||
#define XTENSA_APPTRACE_BLOCK_LEN_GET(_v_) ((_v_) & XTENSA_APPTRACE_BLOCK_LEN_MSK)
|
||||
#define XTENSA_APPTRACE_BLOCK_ID(_id_) (((_id_) & XTENSA_APPTRACE_BLOCK_ID_MSK) << 15)
|
||||
#define XTENSA_APPTRACE_BLOCK_ID_GET(_v_) (((_v_) >> 15) & XTENSA_APPTRACE_BLOCK_ID_MSK)
|
||||
#define XTENSA_APPTRACE_HOST_DATA BIT(22)
|
||||
#define XTENSA_APPTRACE_HOST_CONNECT BIT(23)
|
||||
|
||||
static int esp_xtensa_apptrace_leave_crit_section_start(struct target *target);
|
||||
static int esp_xtensa_apptrace_leave_crit_section_stop(struct target *target);
|
||||
static int esp_xtensa_apptrace_buffs_write(struct target *target,
|
||||
uint32_t bufs_num,
|
||||
uint32_t buf_sz[],
|
||||
const uint8_t *bufs[],
|
||||
uint32_t block_id,
|
||||
bool ack,
|
||||
bool data);
|
||||
|
||||
struct esp32_apptrace_hw esp_xtensa_apptrace_hw = {
|
||||
.max_block_id = XTENSA_APPTRACE_BLOCK_ID_MAX,
|
||||
.max_block_size_get = esp_xtensa_apptrace_block_max_size_get,
|
||||
.status_reg_read = esp_xtensa_apptrace_status_reg_read,
|
||||
.ctrl_reg_write = esp_xtensa_apptrace_ctrl_reg_write,
|
||||
.ctrl_reg_read = esp_xtensa_apptrace_ctrl_reg_read,
|
||||
.data_len_read = esp_xtensa_apptrace_data_len_read,
|
||||
.data_read = esp_xtensa_apptrace_data_read,
|
||||
.usr_block_max_size_get = esp_xtensa_apptrace_usr_block_max_size_get,
|
||||
.buffs_write = esp_xtensa_apptrace_buffs_write,
|
||||
.leave_trace_crit_section_start = esp_xtensa_apptrace_leave_crit_section_start,
|
||||
.leave_trace_crit_section_stop = esp_xtensa_apptrace_leave_crit_section_stop,
|
||||
};
|
||||
|
||||
uint32_t esp_xtensa_apptrace_block_max_size_get(struct target *target)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
struct xtensa_trace_status trace_status;
|
||||
struct xtensa_trace_config trace_config;
|
||||
uint32_t max_trace_block_sz;
|
||||
|
||||
int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to read TRAX status (%d)!", res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
max_trace_block_sz = BIT(((trace_status.stat >> 8) & 0x1f) - 2) * 4;
|
||||
res = xtensa_dm_trace_config_read(&xtensa->dbg_mod, &trace_config);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to read TRAX config (%d)!", res);
|
||||
return 0;
|
||||
}
|
||||
LOG_DEBUG("ctrl=0x%" PRIx32 " memadrstart=0x%" PRIx32 " memadrend=0x%" PRIx32 " traxadr=0x%" PRIx32,
|
||||
trace_config.ctrl,
|
||||
trace_config.memaddr_start,
|
||||
trace_config.memaddr_end,
|
||||
trace_config.addr);
|
||||
|
||||
return max_trace_block_sz;
|
||||
}
|
||||
|
||||
uint32_t esp_xtensa_apptrace_usr_block_max_size_get(struct target *target)
|
||||
{
|
||||
return esp_xtensa_apptrace_block_max_size_get(target) - sizeof(struct esp_apptrace_host2target_hdr);
|
||||
}
|
||||
|
||||
int esp_xtensa_apptrace_data_len_read(struct target *target,
|
||||
uint32_t *block_id,
|
||||
uint32_t *len)
|
||||
{
|
||||
return esp_xtensa_apptrace_ctrl_reg_read(target, block_id, len, NULL);
|
||||
}
|
||||
|
||||
int esp_xtensa_apptrace_usr_block_write(struct target *target,
|
||||
uint32_t block_id,
|
||||
const uint8_t *data,
|
||||
uint32_t size)
|
||||
{
|
||||
return esp_apptrace_usr_block_write(&esp_xtensa_apptrace_hw, target, block_id, data, size);
|
||||
}
|
||||
|
||||
static int esp_xtensa_apptrace_data_reverse_read(struct xtensa *xtensa,
|
||||
uint32_t size,
|
||||
uint8_t *buffer,
|
||||
uint8_t *unal_bytes)
|
||||
{
|
||||
int res = 0;
|
||||
uint32_t rd_sz = ALIGN_UP(size, 4);
|
||||
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, (xtensa->core_config->trace.mem_sz - rd_sz) / 4);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
if (!IS_ALIGNED(size, 4)) {
|
||||
res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, unal_bytes);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
for (unsigned int i = size / 4; i != 0; i--) {
|
||||
res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, &buffer[(i - 1) * 4]);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_apptrace_data_normal_read(struct xtensa *xtensa,
|
||||
uint32_t size,
|
||||
uint8_t *buffer,
|
||||
uint8_t *unal_bytes)
|
||||
{
|
||||
int res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, 0);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
for (unsigned int i = 0; i < size / 4; i++) {
|
||||
res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, &buffer[i * 4]);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
if (!IS_ALIGNED(size, 4)) {
|
||||
res = xtensa_queue_dbg_reg_read(xtensa, XDMREG_TRAXDATA, unal_bytes);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_xtensa_apptrace_data_read(struct target *target,
|
||||
uint32_t size,
|
||||
uint8_t *buffer,
|
||||
uint32_t block_id,
|
||||
bool ack)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
int res;
|
||||
uint32_t tmp = XTENSA_APPTRACE_HOST_CONNECT | XTENSA_APPTRACE_BLOCK_ID(block_id) |
|
||||
XTENSA_APPTRACE_BLOCK_LEN(0);
|
||||
uint8_t unal_bytes[4];
|
||||
|
||||
LOG_DEBUG("Read data on target (%s)", target_name(target));
|
||||
if (xtensa->core_config->trace.reversed_mem_access)
|
||||
res = esp_xtensa_apptrace_data_reverse_read(xtensa, size, buffer, unal_bytes);
|
||||
else
|
||||
res = esp_xtensa_apptrace_data_normal_read(xtensa, size, buffer, unal_bytes);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
if (ack) {
|
||||
LOG_DEBUG("Ack block %" PRIu32 " target (%s)!", block_id, target_name(target));
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
|
||||
res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to exec JTAG queue!");
|
||||
return res;
|
||||
}
|
||||
if (!IS_ALIGNED(size, 4)) {
|
||||
/* copy the last unaligned bytes */
|
||||
memcpy(buffer + ALIGN_DOWN(size, 4), unal_bytes, size & 0x3UL);
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_xtensa_apptrace_ctrl_reg_write(struct target *target,
|
||||
uint32_t block_id,
|
||||
uint32_t len,
|
||||
bool conn,
|
||||
bool data)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
uint32_t tmp = (conn ? XTENSA_APPTRACE_HOST_CONNECT : 0) |
|
||||
(data ? XTENSA_APPTRACE_HOST_DATA : 0) | XTENSA_APPTRACE_BLOCK_ID(block_id) |
|
||||
XTENSA_APPTRACE_BLOCK_LEN(len);
|
||||
|
||||
xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
|
||||
xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
|
||||
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to exec JTAG queue!");
|
||||
return res;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_xtensa_apptrace_ctrl_reg_read(struct target *target,
|
||||
uint32_t *block_id,
|
||||
uint32_t *len,
|
||||
bool *conn)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
uint8_t tmp[4];
|
||||
|
||||
xtensa_queue_dbg_reg_read(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
|
||||
xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
|
||||
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
uint32_t val = target_buffer_get_u32(target, tmp);
|
||||
if (block_id)
|
||||
*block_id = XTENSA_APPTRACE_BLOCK_ID_GET(val);
|
||||
if (len)
|
||||
*len = XTENSA_APPTRACE_BLOCK_LEN_GET(val);
|
||||
if (conn)
|
||||
*conn = val & XTENSA_APPTRACE_HOST_CONNECT;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_xtensa_apptrace_status_reg_read(struct target *target, uint32_t *stat)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
uint8_t tmp[4];
|
||||
|
||||
xtensa_queue_dbg_reg_read(xtensa, XTENSA_APPTRACE_STAT_REG, tmp);
|
||||
xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
|
||||
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to exec JTAG queue!");
|
||||
return res;
|
||||
}
|
||||
*stat = buf_get_u32(tmp, 0, 32);
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_xtensa_apptrace_status_reg_write(struct target *target, uint32_t stat)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
|
||||
xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_STAT_REG, stat);
|
||||
xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
|
||||
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to exec JTAG queue!");
|
||||
return res;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_swdbg_activate(struct target *target, int enab)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
|
||||
xtensa_queue_dbg_reg_write(xtensa, enab ? XDMREG_DCRSET : XDMREG_DCRCLR, OCDDCR_DEBUGSWACTIVE);
|
||||
xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
|
||||
int res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("%s: writing DCR failed!", target->cmd_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_apptrace_leave_crit_section_start(struct target *target)
|
||||
{
|
||||
/* TODO: not sure that we need this, but it seems that we fail to leave tracing critical
|
||||
*section w/o this */
|
||||
int res = esp_xtensa_swdbg_activate(target, 1 /*enable*/);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to activate SW debug (%d)!", res);
|
||||
return res;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_apptrace_leave_crit_section_stop(struct target *target)
|
||||
{
|
||||
int res = esp_xtensa_swdbg_activate(target, 0 /*disable*/);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to activate SW debug (%d)!", res);
|
||||
return res;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_apptrace_queue_reverse_write(struct target *target, uint32_t bufs_num,
|
||||
uint32_t buf_sz[], const uint8_t *bufs[])
|
||||
{
|
||||
int res = ERROR_OK;
|
||||
uint32_t cached_bytes = 0, total_sz = 0;
|
||||
uint8_t cached_data8[sizeof(uint32_t)] = { 0 };
|
||||
uint32_t cached_data32 = 0;
|
||||
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
|
||||
for (uint32_t i = 0; i < bufs_num; i++)
|
||||
total_sz += buf_sz[i];
|
||||
if (!IS_ALIGNED(total_sz, 4)) {
|
||||
cached_bytes = sizeof(uint32_t) - (total_sz & 0x3UL);
|
||||
total_sz = ALIGN_UP(total_sz, 4);
|
||||
}
|
||||
xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, (xtensa->core_config->trace.mem_sz - total_sz) / 4);
|
||||
for (uint32_t i = bufs_num; i > 0; i--) {
|
||||
uint32_t bsz = buf_sz[i - 1];
|
||||
const uint8_t *cur_buf = &bufs[i - 1][bsz];
|
||||
uint32_t bytes_to_cache;
|
||||
/* if there are cached bytes from the previous buffer, combine them with the last
|
||||
* from the current buffer */
|
||||
if (cached_bytes) {
|
||||
if ((cached_bytes + bsz) < sizeof(uint32_t))
|
||||
bytes_to_cache = bsz;
|
||||
else
|
||||
bytes_to_cache = sizeof(uint32_t) - cached_bytes;
|
||||
memcpy(&cached_data8[sizeof(uint32_t) - cached_bytes - bytes_to_cache],
|
||||
cur_buf - bytes_to_cache,
|
||||
bytes_to_cache);
|
||||
cached_data32 = target_buffer_get_u32(target, cached_data8);
|
||||
cached_bytes += bytes_to_cache;
|
||||
if (cached_bytes < sizeof(uint32_t))
|
||||
continue;
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
bsz -= bytes_to_cache;
|
||||
cur_buf -= bytes_to_cache;
|
||||
memset(cached_data8, 0x00, sizeof(cached_data8));
|
||||
cached_bytes = 0;
|
||||
}
|
||||
/* write full dwords */
|
||||
for (unsigned int k = bsz; k >= sizeof(uint32_t); k -= sizeof(uint32_t)) {
|
||||
uint32_t temp = target_buffer_get_u32(target, cur_buf - sizeof(uint32_t));
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, temp);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
cur_buf -= sizeof(uint32_t);
|
||||
}
|
||||
/* if there are bytes to be cached (1..3) */
|
||||
bytes_to_cache = bsz & 0x3UL;
|
||||
if (bytes_to_cache > 0) {
|
||||
if (bytes_to_cache + cached_bytes >= sizeof(uint32_t)) {
|
||||
/* filling the cache buffer from the end to beginning */
|
||||
uint32_t to_copy = sizeof(uint32_t) - cached_bytes;
|
||||
memcpy(&cached_data8[0], cur_buf - to_copy, to_copy);
|
||||
cached_data32 = target_buffer_get_u32(target, cached_data8);
|
||||
/* write full word of cached bytes */
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
/* cache remaining bytes */
|
||||
memset(cached_data8, 0x00, sizeof(cached_data8));
|
||||
cur_buf -= to_copy;
|
||||
to_copy = bytes_to_cache + cached_bytes - sizeof(uint32_t);
|
||||
memcpy(&cached_data8[sizeof(uint32_t) - to_copy], cur_buf - to_copy, to_copy);
|
||||
cached_bytes = to_copy;
|
||||
} else {
|
||||
/* filling the cache buffer from the end to beginning */
|
||||
memcpy(&cached_data8[sizeof(uint32_t) - cached_bytes - bytes_to_cache],
|
||||
cur_buf - bytes_to_cache,
|
||||
bytes_to_cache);
|
||||
cached_bytes += bytes_to_cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_apptrace_queue_normal_write(struct target *target, uint32_t bufs_num,
|
||||
uint32_t buf_sz[], const uint8_t *bufs[])
|
||||
{
|
||||
int res = ERROR_OK;
|
||||
uint32_t cached_bytes = 0;
|
||||
uint8_t cached_data8[4] = { 0 };
|
||||
uint32_t cached_data32 = 0;
|
||||
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
|
||||
/* | 1 | 2 | 1 | 2 | 4 |.......|
|
||||
* | 4 | 4 | 4 | */
|
||||
xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXADDR, 0);
|
||||
for (unsigned int i = 0; i < bufs_num; i++) {
|
||||
uint32_t bsz = buf_sz[i];
|
||||
const uint8_t *cur_buf = bufs[i];
|
||||
uint32_t bytes_to_cache;
|
||||
/* if there are cached bytes from the previous buffer, combine them with the last
|
||||
* from the current buffer */
|
||||
if (cached_bytes) {
|
||||
if ((cached_bytes + bsz) < sizeof(uint32_t))
|
||||
bytes_to_cache = bsz;
|
||||
else
|
||||
bytes_to_cache = sizeof(uint32_t) - cached_bytes;
|
||||
memcpy(&cached_data8[cached_bytes], cur_buf, bytes_to_cache);
|
||||
cached_bytes += bytes_to_cache;
|
||||
if (cached_bytes < sizeof(uint32_t))
|
||||
continue;
|
||||
cached_data32 = target_buffer_get_u32(target, cached_data8);
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
bsz -= bytes_to_cache;
|
||||
cur_buf += bytes_to_cache;
|
||||
memset(cached_data8, 0x00, sizeof(cached_data8));
|
||||
cached_bytes = 0;
|
||||
}
|
||||
/* write full dwords */
|
||||
for (unsigned int k = 0; (k + sizeof(uint32_t)) <= bsz; k += sizeof(uint32_t)) {
|
||||
uint32_t temp = target_buffer_get_u32(target, cur_buf);
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, temp);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
cur_buf += sizeof(uint32_t);
|
||||
}
|
||||
/* if there are bytes to be cached (1..3) */
|
||||
bytes_to_cache = bsz & 0x3UL;
|
||||
if (bytes_to_cache > 0) {
|
||||
if (bytes_to_cache + cached_bytes >= sizeof(uint32_t)) {
|
||||
memcpy(&cached_data8[0], cur_buf, sizeof(uint32_t) - cached_bytes);
|
||||
cached_data32 = target_buffer_get_u32(target, cached_data8);
|
||||
/* write full word of cached bytes */
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
/* cache remaining bytes */
|
||||
memset(cached_data8, 0x00, sizeof(cached_data8));
|
||||
cur_buf += sizeof(uint32_t) - cached_bytes;
|
||||
cached_bytes = bytes_to_cache + cached_bytes - sizeof(uint32_t);
|
||||
memcpy(&cached_data8[0], cur_buf, cached_bytes);
|
||||
} else {
|
||||
memcpy(&cached_data8[cached_bytes], cur_buf, bytes_to_cache);
|
||||
cached_bytes += bytes_to_cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cached_bytes) {
|
||||
/* write remaining cached bytes */
|
||||
cached_data32 = target_buffer_get_u32(target, cached_data8);
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XDMREG_TRAXDATA, cached_data32);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_apptrace_buffs_write(struct target *target,
|
||||
uint32_t bufs_num,
|
||||
uint32_t buf_sz[],
|
||||
const uint8_t *bufs[],
|
||||
uint32_t block_id,
|
||||
bool ack,
|
||||
bool data)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
int res = ERROR_OK;
|
||||
uint32_t tmp = XTENSA_APPTRACE_HOST_CONNECT |
|
||||
(data ? XTENSA_APPTRACE_HOST_DATA : 0) | XTENSA_APPTRACE_BLOCK_ID(block_id) |
|
||||
XTENSA_APPTRACE_BLOCK_LEN(0);
|
||||
|
||||
if (xtensa->core_config->trace.reversed_mem_access)
|
||||
res = esp_xtensa_apptrace_queue_reverse_write(target, bufs_num, buf_sz, bufs);
|
||||
else
|
||||
res = esp_xtensa_apptrace_queue_normal_write(target, bufs_num, buf_sz, bufs);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
if (ack) {
|
||||
LOG_DEBUG("Ack block %" PRId32 " on target (%s)!", block_id, target_name(target));
|
||||
res = xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
|
||||
res = xtensa_dm_queue_execute(&xtensa->dbg_mod);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to exec JTAG queue!");
|
||||
return res;
|
||||
}
|
||||
return ERROR_OK;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/***************************************************************************
|
||||
* Xtensa application tracing module for OpenOCD *
|
||||
* Copyright (C) 2017 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_TARGET_ESP_XTENSA_APPTRACE_H
|
||||
#define OPENOCD_TARGET_ESP_XTENSA_APPTRACE_H
|
||||
|
||||
#include "esp32_apptrace.h"
|
||||
|
||||
struct esp_xtensa_apptrace_info {
|
||||
const struct esp32_apptrace_hw *hw;
|
||||
};
|
||||
|
||||
extern struct esp32_apptrace_hw esp_xtensa_apptrace_hw;
|
||||
|
||||
int esp_xtensa_apptrace_data_len_read(struct target *target, uint32_t *block_id, uint32_t *len);
|
||||
int esp_xtensa_apptrace_data_read(struct target *target,
|
||||
uint32_t size,
|
||||
uint8_t *buffer,
|
||||
uint32_t block_id,
|
||||
bool ack);
|
||||
int esp_xtensa_apptrace_ctrl_reg_read(struct target *target, uint32_t *block_id, uint32_t *len, bool *conn);
|
||||
int esp_xtensa_apptrace_ctrl_reg_write(struct target *target,
|
||||
uint32_t block_id,
|
||||
uint32_t len,
|
||||
bool conn,
|
||||
bool data);
|
||||
int esp_xtensa_apptrace_status_reg_write(struct target *target, uint32_t stat);
|
||||
int esp_xtensa_apptrace_status_reg_read(struct target *target, uint32_t *stat);
|
||||
uint32_t esp_xtensa_apptrace_block_max_size_get(struct target *target);
|
||||
uint32_t esp_xtensa_apptrace_usr_block_max_size_get(struct target *target);
|
||||
int esp_xtensa_apptrace_usr_block_write(struct target *target, uint32_t block_id, const uint8_t *data, uint32_t size);
|
||||
|
||||
#endif /* OPENOCD_TARGET_ESP_XTENSA_APPTRACE_H */
|
Loading…
Reference in New Issue