target/riscv: single DMI accesses via batch

* Eliminates the use of VLA, which is prohibited by `doc/manual
/style.txt`:
Link: c6bb902629/doc/manual/style.txt (L164-L166)

* Unifies DMI access interface.

* Reduces code duplication.

Change-Id: I2d7b0595f171e21062049ff61f76fb5a3c992d11
Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
This commit is contained in:
Evgeniy Naydanov 2024-06-03 20:05:15 +03:00
parent 87c581a80d
commit 9a489be795
5 changed files with 74 additions and 272 deletions

View File

@ -190,8 +190,7 @@ int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
for (size_t i = start_idx; i < batch->used_scans; ++i) {
const int delay = get_delay(batch, i, delays);
riscv_log_dmi_scan(batch->target, delay, batch->fields + i,
/*discard_in*/ false);
riscv_log_dmi_scan(batch->target, delay, batch->fields + i);
}
batch->was_run = true;
@ -199,14 +198,14 @@ int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
return ERROR_OK;
}
void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
bool read_back, enum riscv_scan_delay_class delay_class)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
field->num_bits = riscv_get_dmi_scan_length(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dm_write(batch->target, (char *)field->out_value, address, data);
riscv_fill_dmi_write(batch->target, (char *)field->out_value, address, data);
if (read_back) {
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dm_nop(batch->target, (char *)field->in_value);
@ -218,7 +217,7 @@ void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint3
batch->used_scans++;
}
size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address,
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, uint64_t address,
enum riscv_scan_delay_class delay_class)
{
assert(batch->used_scans < batch->allocated_scans);
@ -226,7 +225,7 @@ size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address,
field->num_bits = riscv_get_dmi_scan_length(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dm_read(batch->target, (char *)field->out_value, address);
riscv_fill_dmi_read(batch->target, (char *)field->out_value, address);
riscv_fill_dm_nop(batch->target, (char *)field->in_value);
batch->delay_classes[batch->used_scans] = delay_class;
batch->last_scan = RISCV_SCAN_TYPE_READ;

View File

@ -190,14 +190,32 @@ int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
size_t riscv_batch_finished_scans(const struct riscv_batch *batch);
/* Adds a DM register write to this batch. */
void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
bool read_back, enum riscv_scan_delay_class delay_class);
static inline void
riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
bool read_back, enum riscv_scan_delay_class delay_type)
{
return riscv_batch_add_dmi_write(batch,
riscv_get_dmi_address(batch->target, address), data,
read_back, delay_type);
}
/* DM register reads must be handled in two parts: the first one schedules a read and
* provides a key, the second one actually obtains the result of the read -
* status (op) and the actual data. */
size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address,
size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, uint64_t address,
enum riscv_scan_delay_class delay_class);
static inline size_t
riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address,
enum riscv_scan_delay_class delay_type)
{
return riscv_batch_add_dmi_read(batch,
riscv_get_dmi_address(batch->target, address), delay_type);
}
unsigned int riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key);
uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key);
@ -213,7 +231,7 @@ bool riscv_batch_was_batch_busy(const struct riscv_batch *batch);
/* TODO: The function is defined in `riscv-013.c`. This is done to reduce the
* diff of the commit. The intention is to move the function definition to
* a separate module (e.g. `riscv013-jtag-dtm.c/h`) in another commit. */
void riscv_log_dmi_scan(const struct target *target, int idle, const struct scan_field *field,
bool discard_in);
void riscv_log_dmi_scan(const struct target *target, int idle,
const struct scan_field *field);
#endif

View File

@ -56,10 +56,7 @@ static int riscv013_invalidate_cached_progbuf(struct target *target);
static int riscv013_execute_progbuf(struct target *target, uint32_t *cmderr);
static void riscv013_fill_dmi_write(struct target *target, char *buf, uint64_t a, uint32_t d);
static void riscv013_fill_dmi_read(struct target *target, char *buf, uint64_t a);
static void riscv013_fill_dmi_nop(struct target *target, char *buf);
static int riscv013_get_dmi_scan_length(struct target *target);
static void riscv013_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d);
static void riscv013_fill_dm_read(struct target *target, char *buf, uint64_t a);
static void riscv013_fill_dm_nop(struct target *target, char *buf);
static unsigned int register_size(struct target *target, enum gdb_regno number);
static int register_read_direct(struct target *target, riscv_reg_t *value,
@ -376,7 +373,9 @@ static unsigned int decode_dmi(const struct target *target, char *text, uint32_t
return 0;
}
void riscv_log_dmi_scan(const struct target *target, int idle, const struct scan_field *field, bool discard_in)
/* TODO: Move this function to "batch.c" and make it static. */
void riscv_log_dmi_scan(const struct target *target, int idle,
const struct scan_field *field)
{
static const char * const op_string[] = {"-", "r", "w", "?"};
static const char * const status_string[] = {"+", "?", "F", "b"};
@ -400,7 +399,7 @@ void riscv_log_dmi_scan(const struct target *target, int idle, const struct scan
field->num_bits, op_string[out_op], out_data, out_address,
status_string[in_op], in_data, in_address, idle);
if (!discard_in && in_op == DTM_DMI_OP_SUCCESS) {
if (in_op == DTM_DMI_OP_SUCCESS) {
char in_decoded[decode_dmi(target, NULL, in_address, in_data) + 1];
decode_dmi(target, in_decoded, in_address, in_data);
/* FIXME: The current code assumes that the hardware
@ -506,220 +505,6 @@ static void decrement_reset_delays_counter(struct target *target, size_t finishe
"resetting learned delays (reset_delays_wait counter expired)");
reset_learned_delays(target);
}
/**
* exec: If this is set, assume the scan results in an execution, so more
* run-test/idle cycles may be required.
*/
static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
uint32_t *data_in, dmi_op_t op, uint32_t address_out, uint32_t data_out,
bool exec)
{
riscv013_info_t *info = get_info(target);
unsigned num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH;
size_t num_bytes = (num_bits + 7) / 8;
uint8_t in[num_bytes];
uint8_t out[num_bytes];
struct scan_field field = {
.num_bits = num_bits,
.out_value = out,
.in_value = in
};
riscv_bscan_tunneled_scan_context_t bscan_ctxt;
decrement_reset_delays_counter(target, 1);
memset(in, 0, num_bytes);
memset(out, 0, num_bytes);
if (info->abits == 0) {
LOG_TARGET_ERROR(target, "Can't access DMI because addrbits=0.");
return DMI_STATUS_FAILED;
}
buf_set_u32(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op);
buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
buf_set_u32(out, DTM_DMI_ADDRESS_OFFSET, info->abits, address_out);
/* I wanted to place this code in a different function, but the way JTAG command
queueing works in the jtag handling functions, the scan fields either have to be
heap allocated, global/static, or else they need to stay on the stack until
the jtag_execute_queue() call. Heap or static fields in this case doesn't seem
the best fit. Declaring stack based field values in a subsidiary function call wouldn't
work. */
if (bscan_tunnel_ir_width != 0) {
riscv_add_bscan_tunneled_scan(target, &field, &bscan_ctxt);
} else {
/* Assume dbus is already selected. */
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
}
int idle_count = exec
? riscv_scan_get_delay(&info->learned_delays, RISCV_DELAY_ABSTRACT_COMMAND)
: riscv_scan_get_delay(&info->learned_delays, RISCV_DELAY_BASE);
if (idle_count)
jtag_add_runtest(idle_count, TAP_IDLE);
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("dmi_scan failed jtag scan");
if (data_in)
*data_in = ~0;
return DMI_STATUS_FAILED;
}
if (bscan_tunnel_ir_width != 0) {
/* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
buffer_shr(in, num_bytes, 1);
}
if (data_in)
*data_in = buf_get_u32(in, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
if (address_in)
*address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits);
riscv_log_dmi_scan(target, idle_count, &field, /*discard_in*/ !data_in);
return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
}
/**
* @param target
* @param data_in The data we received from the target.
* @param dmi_busy_encountered
* If non-NULL, will be updated to reflect whether DMI busy was
* encountered while executing this operation or not.
* @param op The operation to perform (read/write/nop).
* @param address The address argument to that operation.
* @param data_out The data to send to the target.
* @param timeout_sec
* @param exec When true, this scan will execute something, so extra RTI
* cycles may be added.
* @param ensure_success
* Scan a nop after the requested operation, ensuring the
* DMI operation succeeded.
*/
static int dmi_op_timeout(struct target *target, uint32_t *data_in,
bool *dmi_busy_encountered, int op, uint32_t address,
uint32_t data_out, int timeout_sec, bool exec, bool ensure_success)
{
select_dmi(target);
dmi_status_t status;
if (dmi_busy_encountered)
*dmi_busy_encountered = false;
const char *op_name;
switch (op) {
case DMI_OP_NOP:
op_name = "nop";
break;
case DMI_OP_READ:
op_name = "read";
break;
case DMI_OP_WRITE:
op_name = "write";
break;
default:
LOG_ERROR("Invalid DMI operation: %d", op);
return ERROR_FAIL;
}
keep_alive();
time_t start = time(NULL);
/* This first loop performs the request. Note that if for some reason this
* stays busy, it is actually due to the previous access. */
while (1) {
status = dmi_scan(target, NULL, NULL, op, address, data_out,
exec);
if (status == DMI_STATUS_BUSY) {
int result = increase_dmi_busy_delay(target);
if (result != ERROR_OK)
return result;
if (dmi_busy_encountered)
*dmi_busy_encountered = true;
} else if (status == DMI_STATUS_SUCCESS) {
break;
} else {
dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */);
break;
}
if (time(NULL) - start > timeout_sec)
return ERROR_TIMEOUT_REACHED;
}
if (status != DMI_STATUS_SUCCESS) {
LOG_TARGET_ERROR(target, "Failed DMI %s at 0x%x; status=%d", op_name, address, status);
return ERROR_FAIL;
}
if (ensure_success) {
/* This second loop ensures the request succeeded, and gets back data.
* Note that NOP can result in a 'busy' result as well, but that would be
* noticed on the next DMI access we do. */
while (1) {
status = dmi_scan(target, NULL, data_in, DMI_OP_NOP, address, 0,
false);
if (status == DMI_STATUS_BUSY) {
int result = increase_dmi_busy_delay(target);
if (result != ERROR_OK)
return result;
if (dmi_busy_encountered)
*dmi_busy_encountered = true;
} else if (status == DMI_STATUS_SUCCESS) {
break;
} else {
if (data_in) {
LOG_TARGET_ERROR(target,
"Failed DMI %s (NOP) at 0x%x; value=0x%x, status=%d",
op_name, address, *data_in, status);
} else {
LOG_TARGET_ERROR(target,
"Failed DMI %s (NOP) at 0x%x; status=%d", op_name, address,
status);
}
dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */);
return ERROR_FAIL;
}
if (time(NULL) - start > timeout_sec)
return ERROR_TIMEOUT_REACHED;
}
}
return ERROR_OK;
}
static int dmi_op(struct target *target, uint32_t *data_in,
bool *dmi_busy_encountered, int op, uint32_t address,
uint32_t data_out, bool exec, bool ensure_success)
{
int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, op,
address, data_out, riscv_get_command_timeout_sec(), exec, ensure_success);
if (result == ERROR_TIMEOUT_REACHED) {
LOG_TARGET_ERROR(target, "DMI operation didn't complete in %d seconds. The target is "
"either really slow or broken. You could increase the "
"timeout with riscv set_command_timeout_sec.",
riscv_get_command_timeout_sec());
return ERROR_FAIL;
}
return result;
}
static int dmi_read(struct target *target, uint32_t *value, uint32_t address)
{
return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, false, true);
}
static int dmi_read_exec(struct target *target, uint32_t *value, uint32_t address)
{
return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, true, true);
}
static int dmi_write(struct target *target, uint32_t address, uint32_t value)
{
return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, false, true);
}
static uint32_t riscv013_get_dmi_address(const struct target *target, uint32_t address)
{
@ -731,12 +516,22 @@ static uint32_t riscv013_get_dmi_address(const struct target *target, uint32_t a
return address + base;
}
static int batch_run_timeout(struct target *target, struct riscv_batch *batch);
static int dmi_read(struct target *target, uint32_t *value, uint32_t address)
{
struct riscv_batch *batch = riscv_batch_alloc(target, 1);
riscv_batch_add_dmi_read(batch, address, RISCV_DELAY_BASE);
int res = batch_run_timeout(target, batch);
if (res == ERROR_OK && value)
*value = riscv_batch_get_dmi_read_data(batch, 0);
riscv_batch_free(batch);
return res;
}
static int dm_read(struct target *target, uint32_t *value, uint32_t address)
{
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
return dmi_read(target, value, address + dm->base);
return dmi_read(target, value, riscv013_get_dmi_address(target, address));
}
static int dm_read_exec(struct target *target, uint32_t *value, uint32_t address)
@ -744,16 +539,29 @@ static int dm_read_exec(struct target *target, uint32_t *value, uint32_t address
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
struct riscv_batch *batch = riscv_batch_alloc(target, 1);
riscv_batch_add_dm_read(batch, address, RISCV_DELAY_ABSTRACT_COMMAND);
dm->abstract_cmd_maybe_busy = true;
return dmi_read_exec(target, value, address + dm->base);
int res = batch_run_timeout(target, batch);
if (res == ERROR_OK && value)
*value = riscv_batch_get_dmi_read_data(batch, 0);
riscv_batch_free(batch);
return res;
}
static int dmi_write(struct target *target, uint32_t address, uint32_t value)
{
struct riscv_batch *batch = riscv_batch_alloc(target, 1);
riscv_batch_add_dmi_write(batch, address, value, /*read_back*/ true,
RISCV_DELAY_BASE);
int res = batch_run_timeout(target, batch);
riscv_batch_free(batch);
return res;
}
static int dm_write(struct target *target, uint32_t address, uint32_t value)
{
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
return dmi_write(target, address + dm->base, value);
return dmi_write(target, riscv013_get_dmi_address(target, address), value);
}
static bool check_dbgbase_exists(struct target *target)
@ -919,8 +727,6 @@ clear_cmderr:
return res;
}
static int batch_run_timeout(struct target *target, struct riscv_batch *batch);
static int execute_abstract_command(struct target *target, uint32_t command,
uint32_t *cmderr)
{
@ -3095,8 +2901,8 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->write_progbuf = &riscv013_write_progbuf;
generic_info->execute_progbuf = &riscv013_execute_progbuf;
generic_info->invalidate_cached_progbuf = &riscv013_invalidate_cached_progbuf;
generic_info->fill_dm_write = &riscv013_fill_dm_write;
generic_info->fill_dm_read = &riscv013_fill_dm_read;
generic_info->fill_dmi_write = &riscv013_fill_dmi_write;
generic_info->fill_dmi_read = &riscv013_fill_dmi_read;
generic_info->fill_dm_nop = &riscv013_fill_dm_nop;
generic_info->get_dmi_scan_length = &riscv013_get_dmi_scan_length;
generic_info->authdata_read = &riscv013_authdata_read;
@ -5533,7 +5339,7 @@ static void riscv013_fill_dmi_read(struct target *target, char *buf, uint64_t a)
buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
}
static void riscv013_fill_dmi_nop(struct target *target, char *buf)
static void riscv013_fill_dm_nop(struct target *target, char *buf)
{
RISCV013_INFO(info);
buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP);
@ -5547,27 +5353,6 @@ static int riscv013_get_dmi_scan_length(struct target *target)
return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
}
void riscv013_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d)
{
dm013_info_t *dm = get_dm(target);
if (!dm)
return;
riscv013_fill_dmi_write(target, buf, a + dm->base, d);
}
void riscv013_fill_dm_read(struct target *target, char *buf, uint64_t a)
{
dm013_info_t *dm = get_dm(target);
if (!dm)
return;
riscv013_fill_dmi_read(target, buf, a + dm->base);
}
void riscv013_fill_dm_nop(struct target *target, char *buf)
{
riscv013_fill_dmi_nop(target, buf);
}
static int maybe_execute_fence_i(struct target *target)
{
if (has_sufficient_progbuf(target, 2))

View File

@ -5587,16 +5587,16 @@ int riscv_execute_progbuf(struct target *target, uint32_t *cmderr)
return r->execute_progbuf(target, cmderr);
}
void riscv_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d)
void riscv_fill_dmi_write(struct target *target, char *buf, uint64_t a, uint32_t d)
{
RISCV_INFO(r);
r->fill_dm_write(target, buf, a, d);
r->fill_dmi_write(target, buf, a, d);
}
void riscv_fill_dm_read(struct target *target, char *buf, uint64_t a)
void riscv_fill_dmi_read(struct target *target, char *buf, uint64_t a)
{
RISCV_INFO(r);
r->fill_dm_read(target, buf, a);
r->fill_dmi_read(target, buf, a);
}
void riscv_fill_dm_nop(struct target *target, char *buf)

View File

@ -226,8 +226,8 @@ struct riscv_info {
int (*execute_progbuf)(struct target *target, uint32_t *cmderr);
int (*invalidate_cached_progbuf)(struct target *target);
int (*get_dmi_scan_length)(struct target *target);
void (*fill_dm_write)(struct target *target, char *buf, uint64_t a, uint32_t d);
void (*fill_dm_read)(struct target *target, char *buf, uint64_t a);
void (*fill_dmi_write)(struct target *target, char *buf, uint64_t a, uint32_t d);
void (*fill_dmi_read)(struct target *target, char *buf, uint64_t a);
void (*fill_dm_nop)(struct target *target, char *buf);
int (*authdata_read)(struct target *target, uint32_t *value, unsigned int index);
@ -408,8 +408,8 @@ int riscv_write_progbuf(struct target *target, int index, riscv_insn_t insn);
int riscv_execute_progbuf(struct target *target, uint32_t *cmderr);
void riscv_fill_dm_nop(struct target *target, char *buf);
void riscv_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d);
void riscv_fill_dm_read(struct target *target, char *buf, uint64_t a);
void riscv_fill_dmi_write(struct target *target, char *buf, uint64_t a, uint32_t d);
void riscv_fill_dmi_read(struct target *target, char *buf, uint64_t a);
int riscv_get_dmi_scan_length(struct target *target);
uint32_t riscv_get_dmi_address(const struct target *target, uint32_t dm_address);