Skip to content

Commit

Permalink
target/riscv: read abstract args using batch
Browse files Browse the repository at this point in the history
This would elliminate the need for an extra nop in-between the two reads
in case of a 64-bit register.

Change-Id: I2cddc14f7f78181bbda5f931c4e2289cfb7a6674
Signed-off-by: Evgeniy Naydanov <[email protected]>
  • Loading branch information
en-sc committed Mar 22, 2024
1 parent 833df27 commit fbe5f2b
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 42 deletions.
79 changes: 61 additions & 18 deletions src/target/riscv/batch.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ bool riscv_batch_full(struct riscv_batch *batch)
return riscv_batch_available_scans(batch) == 0;
}

static void fill_jtag_queue(const struct riscv_batch *batch)
static void fill_jtag_queue(const struct riscv_batch *batch, size_t start_i)
{
size_t i = 0;
size_t i = start_i;
const idle_count_info_t *idle_counts_entry;
list_for_each_entry(idle_counts_entry, &batch->idle_counts, list) {
const size_t idle_count = idle_counts_entry->idle_count;
Expand All @@ -133,9 +133,9 @@ static void fill_jtag_queue(const struct riscv_batch *batch)
assert(i == batch->used_scans);
}

static void batch_dump_fields(const struct riscv_batch *batch)
static void batch_dump_fields(const struct riscv_batch *batch, size_t start_i)
{
size_t i = 0;
size_t i = start_i;
const idle_count_info_t *idle_counts_entry;
list_for_each_entry(idle_counts_entry, &batch->idle_counts, list) {
const size_t idle_count = idle_counts_entry->idle_count;
Expand All @@ -147,16 +147,12 @@ static void batch_dump_fields(const struct riscv_batch *batch)
assert(i == batch->used_scans);
}

int riscv_batch_run(struct riscv_batch *batch)
static int riscv_batch_run_from(struct riscv_batch *batch, size_t start_i)
{
if (batch->used_scans == 0) {
LOG_TARGET_DEBUG(batch->target, "Ignoring empty batch.");
return ERROR_OK;
}

riscv_batch_add_nop(batch);
LOG_TARGET_DEBUG(batch->target, "Running scans [%zu, %zu)",
start_i, batch->used_scans);

fill_jtag_queue(batch);
fill_jtag_queue(batch, start_i);

keep_alive();

Expand All @@ -169,16 +165,62 @@ int riscv_batch_run(struct riscv_batch *batch)

if (bscan_tunnel_ir_width != 0) {
/* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
for (size_t i = 0; i < batch->used_scans; ++i) {
for (size_t i = start_i; i < batch->used_scans; ++i) {
if ((batch->fields + i)->in_value)
buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1);
}
}

batch_dump_fields(batch);
batch_dump_fields(batch, start_i);
return ERROR_OK;
}

int riscv_batch_run(struct riscv_batch *batch)
{
if (batch->used_scans == 0) {
LOG_TARGET_DEBUG(batch->target, "Ignoring empty batch.");
return ERROR_OK;
}
riscv_batch_add_nop(batch);
return riscv_batch_run_from(batch, 0);
}

bool riscv_batch_was_busy(const struct riscv_batch *batch, size_t scan_i)
{
assert(scan_i < batch->used_scans);
const struct scan_field *field = batch->fields + scan_i;
const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY;
}

size_t riscv_batch_first_busy(const struct riscv_batch *batch)
{
assert(batch->used_scans);
assert(riscv_batch_dmi_busy_encountered(batch));
size_t first_busy = 0;
while (!riscv_batch_was_busy(batch, first_busy))
++first_busy;
return first_busy;
}

int riscv_batch_continue(struct riscv_batch *batch, size_t new_idle)
{
assert(riscv_batch_dmi_busy_encountered(batch));
const size_t busy_scan = riscv_batch_first_busy(batch);
LOG_TARGET_DEBUG(batch->target, "Scan %zu returned busy.", busy_scan);

size_t old_idle;
const int result = riscv_batch_change_idle_used_from_scan(batch,
new_idle, &old_idle, busy_scan);
if (result != ERROR_OK)
return result;

if (new_idle > old_idle)
jtag_add_runtest(new_idle - old_idle, TAP_IDLE);

return riscv_batch_run_from(batch, busy_scan);
}

void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
bool read_back)
{
Expand Down Expand Up @@ -291,9 +333,7 @@ bool riscv_batch_dmi_busy_encountered(const struct riscv_batch *batch)
return false;

assert(batch->last_scan == RISCV_SCAN_TYPE_NOP);
const struct scan_field *field = batch->fields + batch->used_scans - 1;
const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY;
return riscv_batch_was_busy(batch, batch->used_scans - 1);
}

/* Returns the entry in `batch->idle_counts` that defines idle_count for the scan. */
Expand All @@ -309,7 +349,8 @@ static idle_count_info_t *find_idle_counts_entry(struct riscv_batch *batch, size
return entry;
}

int riscv_batch_change_idle_used_from_scan(struct riscv_batch *batch, size_t new_idle, size_t scan_idx)
int riscv_batch_change_idle_used_from_scan(struct riscv_batch *batch, size_t new_idle,
size_t *old_idle, size_t scan_idx)
{
idle_count_info_t * const new_entry = malloc(sizeof(*new_entry));
if (!new_entry) {
Expand All @@ -324,6 +365,8 @@ int riscv_batch_change_idle_used_from_scan(struct riscv_batch *batch, size_t new
/* new entry now defines the range until the scan (non-inclusive) */
new_entry->idle_count = old_entry->idle_count;
/* old entry now defines the range from the scan (inclusive) */
if (old_idle)
*old_idle = old_entry->idle_count;
old_entry->idle_count = new_idle;
LOG_DEBUG("Will use idle == %zu from scan %zu until scan %zu.", old_entry->idle_count,
new_entry->until_scan, old_entry->until_scan);
Expand Down
9 changes: 8 additions & 1 deletion src/target/riscv/batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ bool riscv_batch_full(struct riscv_batch *batch);
/* Executes this scan batch. */
int riscv_batch_run(struct riscv_batch *batch);

int riscv_batch_continue(struct riscv_batch *batch, size_t start_i);

size_t riscv_batch_first_busy(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,
bool read_back);
Expand All @@ -75,11 +79,14 @@ void riscv_batch_add_nop(struct riscv_batch *batch);
/* Returns the number of available scans. */
size_t riscv_batch_available_scans(struct riscv_batch *batch);

/* Return true iff the scan in the batch returned DMI_OP_BUSY. */
bool riscv_batch_was_busy(const struct riscv_batch *batch, size_t scan_i);

/* Return true iff the last scan in the batch returned DMI_OP_BUSY. */
bool riscv_batch_dmi_busy_encountered(const struct riscv_batch *batch);

/* Change the number of idle cycles used starting from the given scan. */
int riscv_batch_change_idle_used_from_scan(struct riscv_batch *batch,
size_t new_idle, size_t scan_idx);
size_t new_idle, size_t *old_idle, size_t scan_idx);

#endif
130 changes: 107 additions & 23 deletions src/target/riscv/riscv-013.c
Original file line number Diff line number Diff line change
Expand Up @@ -948,27 +948,51 @@ static int execute_abstract_command(struct target *target, uint32_t command,
return ERROR_OK;
}

static riscv_reg_t read_abstract_arg(struct target *target, unsigned index,
unsigned size_bits)
static void add_data_regs_read(struct riscv_batch *batch, unsigned int index,
unsigned int size_bits)
{
const unsigned int size_in_words = size_bits / 32;
assert(size_bits % 32 == 0);
const unsigned int offset = index * size_in_words;
for (unsigned int i = 0; i < size_in_words; ++i) {
const unsigned int reg_address = DM_DATA0 + offset + i;
riscv_batch_add_dm_read(batch, reg_address);
}
}

static riscv_reg_t read_data_regs(struct riscv_batch *batch,
unsigned int index, unsigned int size_bits)
{
const unsigned int size_in_words = size_bits / 32;
assert(size_bits % 32 == 0);
assert(size_in_words * sizeof(uint32_t) <= sizeof(riscv_reg_t));
riscv_reg_t value = 0;
uint32_t v;
unsigned offset = index * size_bits / 32;
switch (size_bits) {
default:
LOG_TARGET_ERROR(target, "Unsupported size: %d bits", size_bits);
return ~0;
case 64:
if (dm_read(target, &v, DM_DATA0 + offset + 1) == ERROR_OK)
value |= ((uint64_t)v) << 32;
/* falls through */
case 32:
if (dm_read(target, &v, DM_DATA0 + offset) == ERROR_OK)
value |= v;
for (unsigned int i = 0; i < size_in_words; ++i) {
const uint32_t v = riscv_batch_get_dmi_read_data(batch, i);
value |= (riscv_reg_t)v << (i * 32);
}
return value;
}

static int batch_run_timeout(struct target *target, struct riscv_batch *batch);

static int read_abstract_arg(struct target *target, uint64_t *value,
unsigned int index, unsigned int size_bits)
{
assert(value);
RISCV013_INFO(info);
const unsigned char size_in_words = size_bits / 32;
assert(size_bits % 32 == 0);
struct riscv_batch * const batch = riscv_batch_alloc(target, size_in_words,
info->dmi_busy_delay);
add_data_regs_read(batch, index, size_bits);
int result = batch_run_timeout(target, batch);
if (result == ERROR_OK)
*value = read_data_regs(batch, index, size_bits);
riscv_batch_free(batch);
return result;
}

static int write_abstract_arg(struct target *target, unsigned index,
riscv_reg_t value, unsigned size_bits)
{
Expand Down Expand Up @@ -1065,7 +1089,7 @@ static int register_read_abstract_with_size(struct target *target,
}

if (value)
*value = read_abstract_arg(target, 0, size);
return read_abstract_arg(target, value, 0, size);

return ERROR_OK;
}
Expand Down Expand Up @@ -2526,7 +2550,8 @@ static int maybe_add_delays_reset(const struct target *target, struct riscv_batc
r->reset_delays_wait -= scans_to_run;
return ERROR_OK;
}
const int result = riscv_batch_change_idle_used_from_scan(batch, 0, r->reset_delays_wait);
const int result = riscv_batch_change_idle_used_from_scan(batch, 0,
/*discard old_idle*/ NULL, r->reset_delays_wait);
r->reset_delays_wait = -1;
LOG_TARGET_DEBUG(target, "reset_delays_wait done");
RISCV013_INFO(info);
Expand All @@ -2543,6 +2568,52 @@ static int batch_run(const struct target *target, struct riscv_batch *batch)
return riscv_batch_run(batch);
}

static int batch_run_timeout(struct target *target, struct riscv_batch *batch)
{
RISCV013_INFO(info);
const time_t start = time(NULL);
int result = batch_run(target, batch);
if (result == ERROR_OK && !riscv_batch_dmi_busy_encountered(batch))
return ERROR_OK;

while (time(NULL) - start < riscv_command_timeout_sec) {
/* TODO: In general, r->reset_delays_wait should be decresed here.
* This is not an issue for now, since it can be increased only
* by `riscv reset_delays` command, and the cause for the busy
* to be encountered in the batch is, most probably these
* delays being reset.
* TODO: riscv_batch_continue() will increase the delays for
* the scans for which the delays were defined by the same list
* entry.
* Now consider the batch of three scans:
* 1st scan uses idle_count 1 (defined by the first list entry)
* 2nd scan uses idle_count 2 (defined by the second list entry)
* 3rd scan uses idle_count 1 (defined by the third list entry)
*
* When the batch is executed, a dmi_busy is encountered on the first scan.
* So info->dmi_busy_delay and the idle_count for the first
* scan (in the first list entry) are increased.
* However, the value in the 3rd list entry is not increased,
* meaning it will also encounter dmi_busy and
* info->dmi_busy_delay will be increased twice.
*/
increase_dmi_busy_delay(target);
result = riscv_batch_continue(batch, info->dmi_busy_delay);
if (result != ERROR_OK || !riscv_batch_dmi_busy_encountered(batch))
return result;
}
assert(result == ERROR_OK);
assert(riscv_batch_dmi_busy_encountered(batch));
/* Reset dmi_busy_delay, so the value doesn't get too big. */
LOG_TARGET_DEBUG(target, "dmi_busy_delay is reset.");
info->dmi_busy_delay = 0;
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_command_timeout_sec);
return ERROR_TIMEOUT_REACHED;
}

static int sba_supports_access(struct target *target, unsigned int size_bytes)
{
RISCV013_INFO(info);
Expand Down Expand Up @@ -3580,7 +3651,11 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
if (info->has_aampostincrement == YNM_MAYBE) {
if (result == ERROR_OK) {
/* Safety: double-check that the address was really auto-incremented */
riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
riscv_reg_t new_address;
result = read_abstract_arg(target, &new_address, 1, riscv_xlen(target));
if (result != ERROR_OK)
return result;

if (new_address == address + size) {
LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target.");
info->has_aampostincrement = YNM_YES;
Expand All @@ -3603,7 +3678,10 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
return result;

/* Copy arg0 to buffer (rounded width up to nearest 32) */
riscv_reg_t value = read_abstract_arg(target, 0, width32);
riscv_reg_t value;
result = read_abstract_arg(target, &value, 0, width32);
if (result != ERROR_OK)
return result;
buf_set_u64(p, 0, 8 * size, value);

if (info->has_aampostincrement == YNM_YES)
Expand Down Expand Up @@ -3666,7 +3744,11 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
if (info->has_aampostincrement == YNM_MAYBE) {
if (result == ERROR_OK) {
/* Safety: double-check that the address was really auto-incremented */
riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
riscv_reg_t new_address;
result = read_abstract_arg(target, &new_address, 1, riscv_xlen(target));
if (result != ERROR_OK)
return result;

if (new_address == address + size) {
LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target.");
info->has_aampostincrement = YNM_YES;
Expand Down Expand Up @@ -4040,10 +4122,12 @@ static int read_word_from_dm_data_regs(struct target *target,
struct memory_access_info access, uint32_t index)
{
assert(access.element_size <= 8);
const uint64_t value = read_abstract_arg(target, /*index*/ 0,
uint64_t value;
int result = read_abstract_arg(target, &value, /*index*/ 0,
access.element_size > 4 ? 64 : 32);
set_buffer_and_log_read(access, index, value);
return ERROR_OK;
if (result == ERROR_OK)
set_buffer_and_log_read(access, index, value);
return result;
}

static int read_word_from_s1(struct target *target,
Expand Down

0 comments on commit fbe5f2b

Please sign in to comment.