Skip to content

Commit

Permalink
target/riscv: write registers using batch
Browse files Browse the repository at this point in the history
This allows to eliminate up to two DMI NOPs.

Change-Id: I09a18bd896fce2392d1b65d4efb38b53e334a358
Signed-off-by: Evgeniy Naydanov <evgeniy.naydanov@syntacore.com>
  • Loading branch information
en-sc committed Jun 4, 2024
1 parent b548653 commit 4dbe453
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 100 deletions.
64 changes: 49 additions & 15 deletions src/target/riscv/batch.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans)
out->data_out = NULL;
out->data_in = NULL;
out->fields = NULL;
out->delay_classes = NULL;
out->bscan_ctxt = NULL;
out->read_keys = NULL;

Expand All @@ -55,6 +56,11 @@ struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans)
LOG_ERROR("Failed to allocate fields in RISC-V batch.");
goto alloc_error;
}
out->delay_classes = malloc(sizeof(*out->delay_classes) * scans);
if (!out->delay_classes) {
LOG_ERROR("Failed to allocate delay_classes in RISC-V batch.");
goto alloc_error;
}
if (bscan_tunnel_ir_width != 0) {
out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * scans);
if (!out->bscan_ctxt) {
Expand All @@ -80,6 +86,7 @@ void riscv_batch_free(struct riscv_batch *batch)
free(batch->data_in);
free(batch->data_out);
free(batch->fields);
free(batch->delay_classes);
free(batch->bscan_ctxt);
free(batch->read_keys);
free(batch);
Expand All @@ -101,28 +108,45 @@ static bool riscv_batch_was_scan_busy(const struct riscv_batch *batch,
return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY;
}

static void add_idle_if_increased(struct riscv_batch *batch, size_t new_idle_count)
static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_idx,
struct riscv_scan_delays delays)
{
if (!batch->was_run)
return;
if (batch->used_idle_count <= new_idle_count)
return;
const size_t idle_change = new_idle_count - batch->used_idle_count;
LOG_TARGET_DEBUG(batch->target,
"Idle count increased. Adding %zu idle cycles before the batch.",
/* Get the delay type of the scan that resulted in the busy response.
* Since DMI interactions always end with a NOP, if "start_idx" is zero
* the base delay value is used.
*/
const enum riscv_scan_delay_class delay_class = start_idx > 0
? batch->delay_classes[start_idx - 1]
: RISCV_DELAY_BASE;
const int idle_change = riscv_scan_get_delay(delays, delay_class)
- batch->used_idle_count;
LOG_TARGET_DEBUG(batch->target, "Adding %d idle cycles before the batch.",
idle_change);
jtag_add_runtest(idle_change, TAP_IDLE);
}

static int get_delay(const struct riscv_batch *batch, size_t scan_idx,
struct riscv_scan_delays delays)
{
assert(batch);
assert(scan_idx < batch->used_scans);
const enum riscv_scan_delay_class delay_class = batch->delay_classes[scan_idx];
return riscv_scan_get_delay(delays, delay_class);
}

int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
size_t idle_count, bool resets_delays, size_t reset_delays_after)
struct riscv_scan_delays delays, bool resets_delays,
size_t reset_delays_after)
{
assert(batch->used_scans);
assert(batch->last_scan == RISCV_SCAN_TYPE_NOP);
assert(!batch->was_run || riscv_batch_was_scan_busy(batch, start_idx));
assert(start_idx == 0 || !riscv_batch_was_scan_busy(batch, start_idx - 1));

add_idle_if_increased(batch, idle_count);
if (batch->was_run)
add_idle_before_batch(batch, start_idx, delays);

LOG_TARGET_DEBUG(batch->target, "Running batch of scans [%zu, %zu)",
start_idx, batch->used_scans);
Expand All @@ -135,8 +159,10 @@ int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,

const bool delays_were_reset = resets_delays
&& (i >= reset_delays_after);
if (idle_count > 0 && !delays_were_reset)
jtag_add_runtest(idle_count, TAP_IDLE);
const int delay = get_delay(batch, i, delays);

if (!delays_were_reset)
jtag_add_runtest(delay, TAP_IDLE);
}

keep_alive();
Expand All @@ -156,17 +182,19 @@ int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
}
}

for (size_t i = start_idx; i < batch->used_scans; ++i)
riscv_log_dmi_scan(batch->target, idle_count, batch->fields + i,
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);
}

batch->was_run = true;
batch->used_idle_count = idle_count;
batch->used_idle_count = get_delay(batch, batch->used_scans - 1, delays);
return ERROR_OK;
}

void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
bool read_back)
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;
Expand All @@ -179,11 +207,13 @@ void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint3
} else {
field->in_value = NULL;
}
batch->delay_classes[batch->used_scans] = delay_class;
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
batch->used_scans++;
}

size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address)
size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address,
enum riscv_scan_delay_class delay_class)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
Expand All @@ -192,6 +222,7 @@ size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address)
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_dm_nop(batch->target, (char *)field->in_value);
batch->delay_classes[batch->used_scans] = delay_class;
batch->last_scan = RISCV_SCAN_TYPE_READ;
batch->used_scans++;

Expand Down Expand Up @@ -228,6 +259,9 @@ void riscv_batch_add_nop(struct riscv_batch *batch)
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
riscv_fill_dm_nop(batch->target, (char *)field->out_value);
riscv_fill_dm_nop(batch->target, (char *)field->in_value);
/* DMI NOP never triggers any debug module operation,
* so the shortest (base) delay can be used. */
batch->delay_classes[batch->used_scans] = RISCV_DELAY_BASE;
batch->last_scan = RISCV_SCAN_TYPE_NOP;
batch->used_scans++;
}
Expand Down
83 changes: 76 additions & 7 deletions src/target/riscv/batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,70 @@ enum riscv_scan_type {
RISCV_SCAN_TYPE_WRITE,
};

/* These types are used to specify how many JTAG RTI cycles to add after a
* scan.
*/
enum riscv_scan_delay_class {
/* Delay needed for accessing debug module registers: */
RISCV_DELAY_BASE,
/* Delay for execution of an abstract command: */
RISCV_DELAY_ABSTRACT_COMMAND,
/* Delay for System Bus read operation: */
RISCV_DELAY_SYSBUS_READ,
/* Delay for System Bus write operation: */
RISCV_DELAY_SYSBUS_WRITE,
};

struct riscv_scan_delays {
unsigned int base_delay;
unsigned int ac_delay;
unsigned int sb_read_delay;
unsigned int sb_write_delay;
};

static inline int riscv_scan_get_delay(struct riscv_scan_delays delays,
enum riscv_scan_delay_class delay_class)
{
unsigned int delay;
switch (delay_class) {
case RISCV_DELAY_BASE:
delay = delays.base_delay;
break;
case RISCV_DELAY_ABSTRACT_COMMAND:
delay = delays.ac_delay;
break;
case RISCV_DELAY_SYSBUS_READ:
delay = delays.sb_read_delay;
break;
case RISCV_DELAY_SYSBUS_WRITE:
delay = delays.sb_write_delay;
break;
}
/* The purpose of these delays is to be passed to "jtag_add_runtest()",
* which accepts an "int". */
assert(delay <= INT_MAX);
return delay;
}

static inline void riscv_scan_set_delay(struct riscv_scan_delays *delays,
enum riscv_scan_delay_class delay_class, unsigned int delay)
{
switch (delay_class) {
case RISCV_DELAY_BASE:
delays->base_delay = delay;
return;
case RISCV_DELAY_ABSTRACT_COMMAND:
delays->ac_delay = delay;
return;
case RISCV_DELAY_SYSBUS_READ:
delays->sb_read_delay = delay;
return;
case RISCV_DELAY_SYSBUS_WRITE:
delays->sb_write_delay = delay;
return;
}
}

/* A batch of multiple JTAG scans, which are grouped together to avoid the
* overhead of some JTAG adapters when sending single commands. This is
* designed to support block copies, as that's what we actually need to go
Expand All @@ -27,6 +91,7 @@ struct riscv_batch {
uint8_t *data_out;
uint8_t *data_in;
struct scan_field *fields;
enum riscv_scan_delay_class *delay_classes;

/* If in BSCAN mode, this field will be allocated (one per scan),
and utilized to tunnel all the scans in the batch. If not in
Expand All @@ -48,8 +113,10 @@ struct riscv_batch {
* However, RISC-V DMI "busy" condition could still have occurred.
*/
bool was_run;
/* Idle count used on the last run. Only valid after `was_run` is set. */
size_t used_idle_count;
/* Idle count used by the last scan on the last run. Only valid when
* `was_run` is set.
*/
int used_idle_count;
};

/* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG
Expand All @@ -65,28 +132,30 @@ bool riscv_batch_full(struct riscv_batch *batch);
* If batch is run for the first time, it is expected that "start" is zero.
* It is expected that the batch ends with a DMI NOP operation.
*
* "idle_count" is the number of JTAG Run-Test-Idle cycles to add in-between
* the scans.
* "idle_counts" specifies the number of JTAG Run-Test-Idle cycles to add
* after each scan depending on the delay class of the scan.
*
* If "resets_delays" is true, the algorithm will stop inserting idle cycles
* (JTAG Run-Test-Idle) after "reset_delays_after" number of scans is
* performed. This is useful for stress-testing of RISC-V algorithms in
* OpenOCD that are based on batches.
*/
int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
size_t idle_count, bool resets_delays, size_t reset_delays_after);
struct riscv_scan_delays delays, bool resets_delays,
size_t reset_delays_after);

/* Get the number of scans successfully executed form this batch. */
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,
bool read_back);
bool read_back, enum riscv_scan_delay_class delay_class);

/* 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_dm_read(struct riscv_batch *batch, uint64_t address,
enum riscv_scan_delay_class delay_class);
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);

Expand Down
Loading

0 comments on commit 4dbe453

Please sign in to comment.