Merge pull request #1087 from en-sc/en-sc/delay-types

target/riscv: replace `info->*_delay` with `riscv_scan_delays`
This commit is contained in:
Evgeniy Naydanov 2024-07-03 10:54:28 +03:00 committed by GitHub
commit 4b5668bdaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 167 additions and 110 deletions

View File

@ -29,7 +29,7 @@ struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans)
out->allocated_scans = scans;
out->last_scan = RISCV_SCAN_TYPE_INVALID;
out->was_run = false;
out->used_delay = 0;
out->last_scan_delay = 0;
out->data_out = NULL;
out->data_in = NULL;
@ -109,7 +109,7 @@ static bool riscv_batch_was_scan_busy(const struct riscv_batch *batch,
}
static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_idx,
struct riscv_scan_delays delays)
const struct riscv_scan_delays *delays)
{
if (!batch->was_run)
return;
@ -121,9 +121,9 @@ static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_
? batch->delay_classes[start_idx - 1]
: RISCV_DELAY_BASE;
const unsigned int new_delay = riscv_scan_get_delay(delays, delay_class);
if (new_delay <= batch->used_delay)
if (new_delay <= batch->last_scan_delay)
return;
const unsigned int idle_change = new_delay - batch->used_delay;
const unsigned int idle_change = new_delay - batch->last_scan_delay;
LOG_TARGET_DEBUG(batch->target, "Adding %u idle cycles before the batch.",
idle_change);
assert(idle_change <= INT_MAX);
@ -131,7 +131,7 @@ static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_
}
static int get_delay(const struct riscv_batch *batch, size_t scan_idx,
struct riscv_scan_delays delays)
const struct riscv_scan_delays *delays)
{
assert(batch);
assert(scan_idx < batch->used_scans);
@ -143,7 +143,7 @@ static int get_delay(const struct riscv_batch *batch, size_t scan_idx,
}
int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
struct riscv_scan_delays delays, bool resets_delays,
const struct riscv_scan_delays *delays, bool resets_delays,
size_t reset_delays_after)
{
assert(batch->used_scans);
@ -195,7 +195,7 @@ int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
}
batch->was_run = true;
batch->used_delay = get_delay(batch, batch->used_scans - 1, delays);
batch->last_scan_delay = get_delay(batch, batch->used_scans - 1, delays);
return ERROR_OK;
}

View File

@ -25,40 +25,66 @@ enum riscv_scan_delay_class {
/* Delay for System Bus read operation: */
RISCV_DELAY_SYSBUS_READ,
/* Delay for System Bus write operation: */
RISCV_DELAY_SYSBUS_WRITE,
RISCV_DELAY_SYSBUS_WRITE
};
struct riscv_scan_delays {
/* The purpose of these delays is to be passed to "jtag_add_runtest()",
* which accepts an "int".
* Therefore, they should be no greater then "INT_MAX".
static inline const char *
riscv_scan_delay_class_name(enum riscv_scan_delay_class delay_class)
{
switch (delay_class) {
case RISCV_DELAY_BASE:
return "DM access";
case RISCV_DELAY_ABSTRACT_COMMAND:
return "Abstract Command";
case RISCV_DELAY_SYSBUS_READ:
return "System Bus read";
case RISCV_DELAY_SYSBUS_WRITE:
return "System Bus write";
}
assert(0);
return NULL;
}
/* The scan delay values are passed to "jtag_add_runtest()", which accepts an
* "int". Therefore, the passed value should be no greater than "INT_MAX".
*
* Since the resulting delay value can be a sum of two individual delays,
* individual delays are limited to "INT_MAX / 2" to prevent overflow of the
* final sum.
*/
#define RISCV_SCAN_DELAY_MAX (INT_MAX / 2)
struct riscv_scan_delays {
unsigned int base_delay;
unsigned int ac_delay;
unsigned int sb_read_delay;
unsigned int sb_write_delay;
};
static inline unsigned int riscv_scan_get_delay(struct riscv_scan_delays delays,
static inline unsigned int
riscv_scan_get_delay(const struct riscv_scan_delays *delays,
enum riscv_scan_delay_class delay_class)
{
switch (delay_class) {
case RISCV_DELAY_BASE:
return delays.base_delay;
return delays->base_delay;
case RISCV_DELAY_ABSTRACT_COMMAND:
return delays.ac_delay;
return delays->base_delay + delays->ac_delay;
case RISCV_DELAY_SYSBUS_READ:
return delays.sb_read_delay;
return delays->base_delay + delays->sb_read_delay;
case RISCV_DELAY_SYSBUS_WRITE:
return delays.sb_write_delay;
return delays->base_delay + delays->sb_write_delay;
}
assert(0);
return 0;
}
static inline void riscv_scan_set_delay(struct riscv_scan_delays *delays,
enum riscv_scan_delay_class delay_class, unsigned int delay)
{
assert(delay <= INT_MAX);
assert(delay <= RISCV_SCAN_DELAY_MAX);
LOG_DEBUG("%s delay is set to %u.",
riscv_scan_delay_class_name(delay_class), delay);
switch (delay_class) {
case RISCV_DELAY_BASE:
delays->base_delay = delay;
@ -73,6 +99,25 @@ static inline void riscv_scan_set_delay(struct riscv_scan_delays *delays,
delays->sb_write_delay = delay;
return;
}
assert(0);
}
static inline int riscv_scan_increase_delay(struct riscv_scan_delays *delays,
enum riscv_scan_delay_class delay_class)
{
const unsigned int delay = riscv_scan_get_delay(delays, delay_class);
const unsigned int delay_step = delay / 10 + 1;
if (delay > RISCV_SCAN_DELAY_MAX - delay_step) {
/* It's not clear if this issue actually occurs in real
* use-cases, so stick with a simple solution until the
* first bug report.
*/
LOG_ERROR("Delay for %s (%d) is not increased anymore (maximum was reached).",
riscv_scan_delay_class_name(delay_class), delay);
return ERROR_FAIL;
}
riscv_scan_set_delay(delays, delay_class, delay + delay_step);
return ERROR_OK;
}
/* A batch of multiple JTAG scans, which are grouped together to avoid the
@ -113,7 +158,7 @@ struct riscv_batch {
/* Number of RTI cycles used by the last scan on the last run.
* Only valid when `was_run` is set.
*/
unsigned int used_delay;
unsigned int last_scan_delay;
};
/* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG
@ -138,7 +183,7 @@ bool riscv_batch_full(struct riscv_batch *batch);
* OpenOCD that are based on batches.
*/
int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
struct riscv_scan_delays delays, bool resets_delays,
const struct riscv_scan_delays *delays, bool resets_delays,
size_t reset_delays_after);
/* Get the number of scans successfully executed form this batch. */

View File

@ -169,21 +169,12 @@ typedef struct {
* access. */
unsigned int dtmcs_idle;
/* This value is incremented every time a dbus access comes back as "busy".
* It's used to determine how many run-test/idle cycles to feed the target
* in between accesses. */
unsigned int dmi_busy_delay;
/* Number of run-test/idle cycles to add between consecutive bus master
* reads/writes respectively. */
unsigned int bus_master_write_delay, bus_master_read_delay;
/* This value is increased every time we tried to execute two commands
* consecutively, and the second one failed because the previous hadn't
* completed yet. It's used to add extra run-test/idle cycles after
* starting a command, so we don't have to waste time checking for busy to
* go low. */
unsigned int ac_busy_delay;
/* This structure is used to determine how many run-test/idle to use after
* an access of corresponding "riscv_scan_delay_class".
* Values are incremented every time an access results in a busy
* response.
*/
struct riscv_scan_delays learned_delays;
bool abstract_read_csr_supported;
bool abstract_write_csr_supported;
@ -478,15 +469,25 @@ static int dtmcontrol_scan(struct target *target, uint32_t out, uint32_t *in_ptr
return ERROR_OK;
}
static void increase_dmi_busy_delay(struct target *target)
static int increase_dmi_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
LOG_TARGET_DEBUG(target, "dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcs_idle, info->dmi_busy_delay,
info->ac_busy_delay);
RISCV013_INFO(info);
dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */);
int res = dtmcontrol_scan(target, DTM_DTMCS_DMIRESET,
NULL /* discard result */);
if (res != ERROR_OK)
return res;
res = riscv_scan_increase_delay(&info->learned_delays,
RISCV_DELAY_BASE);
return res;
}
static void reset_learned_delays(struct target *target)
{
RISCV013_INFO(info);
assert(info);
memset(&info->learned_delays, 0, sizeof(info->learned_delays));
}
static void decrement_reset_delays_counter(struct target *target, size_t finished_scans)
@ -503,9 +504,7 @@ static void decrement_reset_delays_counter(struct target *target, size_t finishe
r->reset_delays_wait = -1;
LOG_TARGET_DEBUG(target,
"resetting learned delays (reset_delays_wait counter expired)");
RISCV013_INFO(info);
info->dmi_busy_delay = 0;
info->ac_busy_delay = 0;
reset_learned_delays(target);
}
/**
* exec: If this is set, assume the scan results in an execution, so more
@ -554,9 +553,9 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
}
int idle_count = info->dmi_busy_delay;
if (exec)
idle_count += info->ac_busy_delay;
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);
@ -635,7 +634,9 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
status = dmi_scan(target, NULL, NULL, op, address, data_out,
exec);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
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) {
@ -661,7 +662,9 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
status = dmi_scan(target, NULL, data_in, DMI_OP_NOP, address, 0,
false);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
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) {
@ -837,13 +840,11 @@ static int dmstatus_read(struct target *target, uint32_t *dmstatus,
return result;
}
static void increase_ac_busy_delay(struct target *target)
static int increase_ac_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
LOG_TARGET_DEBUG(target, "dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcs_idle, info->dmi_busy_delay,
info->ac_busy_delay);
return riscv_scan_increase_delay(&info->learned_delays,
RISCV_DELAY_ABSTRACT_COMMAND);
}
static uint32_t __attribute__((unused)) abstract_register_size(unsigned width)
@ -911,13 +912,13 @@ static int dm013_select_target(struct target *target)
return dm013_select_hart(target, info->index);
}
#define EXECUTE_ABSTRACT_COMMAND_BATCH_SIZE 2
#define ABSTRACT_COMMAND_BATCH_SIZE 2
static size_t abstract_cmd_fill_batch(struct riscv_batch *batch,
uint32_t command)
{
assert(riscv_batch_available_scans(batch)
>= EXECUTE_ABSTRACT_COMMAND_BATCH_SIZE);
>= ABSTRACT_COMMAND_BATCH_SIZE);
riscv_batch_add_dm_write(batch, DM_COMMAND, command, /* read_back */ true,
RISCV_DELAY_ABSTRACT_COMMAND);
return riscv_batch_add_dm_read(batch, DM_ABSTRACTCS, RISCV_DELAY_BASE);
@ -935,7 +936,9 @@ static int abstract_cmd_batch_check_and_clear_cmderr(struct target *target,
res = wait_for_idle(target, &abstractcs);
if (res != ERROR_OK)
goto clear_cmderr;
increase_ac_busy_delay(target);
res = increase_ac_busy_delay(target);
if (res != ERROR_OK)
goto clear_cmderr;
}
*cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR);
if (*cmderr == CMDERR_NONE)
@ -975,7 +978,7 @@ static int execute_abstract_command(struct target *target, uint32_t command,
return ERROR_FAIL;
struct riscv_batch *batch = riscv_batch_alloc(target,
EXECUTE_ABSTRACT_COMMAND_BATCH_SIZE);
ABSTRACT_COMMAND_BATCH_SIZE);
const size_t abstractcs_read_key = abstract_cmd_fill_batch(batch, command);
/* Abstract commands are executed while running the batch. */
@ -1203,7 +1206,7 @@ static int register_write_abstract(struct target *target, enum gdb_regno number,
assert(size_bits % 32 == 0);
const unsigned int size_in_words = size_bits / 32;
const unsigned int batch_size = size_in_words
+ EXECUTE_ABSTRACT_COMMAND_BATCH_SIZE;
+ ABSTRACT_COMMAND_BATCH_SIZE;
struct riscv_batch * const batch = riscv_batch_alloc(target, batch_size);
abstract_data_write_fill_batch(batch, value, /*index*/ 0, size_bits);
@ -2757,29 +2760,13 @@ static int sb_write_address(struct target *target, target_addr_t address,
(uint32_t)address, false, ensure_success);
}
/* TODO: store delays in "struct riscv_scan_delays" and remove this function. */
struct riscv_scan_delays get_scan_delays(struct target *target)
{
RISCV013_INFO(info);
assert(info);
struct riscv_scan_delays delays;
riscv_scan_set_delay(&delays, RISCV_DELAY_BASE, info->dmi_busy_delay);
riscv_scan_set_delay(&delays, RISCV_DELAY_ABSTRACT_COMMAND, info->dmi_busy_delay +
info->ac_busy_delay);
riscv_scan_set_delay(&delays, RISCV_DELAY_SYSBUS_READ, info->dmi_busy_delay +
info->bus_master_read_delay);
riscv_scan_set_delay(&delays, RISCV_DELAY_SYSBUS_WRITE, info->dmi_busy_delay +
info->bus_master_write_delay);
return delays;
}
static int batch_run(struct target *target, struct riscv_batch *batch)
{
RISCV_INFO(r);
RISCV013_INFO(info);
select_dmi(target);
riscv_batch_add_nop(batch);
const int result = riscv_batch_run_from(batch, 0,
get_scan_delays(target),
const int result = riscv_batch_run_from(batch, 0, &info->learned_delays,
/*resets_delays*/ r->reset_delays_wait >= 0,
r->reset_delays_wait);
/* TODO: To use `riscv_batch_finished_scans()` here, it is needed for
@ -2796,18 +2783,18 @@ static int batch_run(struct target *target, struct riscv_batch *batch)
static int batch_run_timeout(struct target *target, struct riscv_batch *batch)
{
RISCV013_INFO(info);
select_dmi(target);
riscv_batch_add_nop(batch);
size_t finished_scans = 0;
const time_t start = time(NULL);
const unsigned int old_dmi_busy_delay = info->dmi_busy_delay;
const unsigned int old_base_delay = riscv_scan_get_delay(&info->learned_delays,
RISCV_DELAY_BASE);
int result;
do {
RISCV_INFO(r);
result = riscv_batch_run_from(batch, finished_scans,
get_scan_delays(target),
&info->learned_delays,
/*resets_delays*/ r->reset_delays_wait >= 0,
r->reset_delays_wait);
const size_t new_finished_scans = riscv_batch_finished_scans(batch);
@ -2820,16 +2807,20 @@ static int batch_run_timeout(struct target *target, struct riscv_batch *batch)
assert(finished_scans == batch->used_scans);
return ERROR_OK;
}
increase_dmi_busy_delay(target);
result = increase_dmi_busy_delay(target);
if (result != ERROR_OK)
return result;
} while (time(NULL) - start < riscv_command_timeout_sec);
assert(result == ERROR_OK);
assert(riscv_batch_was_batch_busy(batch));
/* Reset dmi_busy_delay, so the value doesn't get too big. */
LOG_TARGET_DEBUG(target, "dmi_busy_delay is restored to %u.",
old_dmi_busy_delay);
info->dmi_busy_delay = old_dmi_busy_delay;
LOG_TARGET_DEBUG(target, "%s delay is restored to %u.",
riscv_scan_delay_class_name(RISCV_DELAY_BASE),
old_base_delay);
riscv_scan_set_delay(&info->learned_delays, RISCV_DELAY_BASE,
old_base_delay);
LOG_TARGET_ERROR(target, "DMI operation didn't complete in %d seconds. "
"The target is either really slow or broken. You could increase "
@ -2968,7 +2959,11 @@ static int sample_memory_bus_v1(struct target *target,
* with a larger DMI delay. */
unsigned int sbcs_read_op = riscv_batch_get_dmi_read_op(batch, sbcs_read_index);
if (sbcs_read_op == DTM_DMI_OP_BUSY) {
increase_dmi_busy_delay(target);
result = increase_dmi_busy_delay(target);
if (result != ERROR_OK) {
riscv_batch_free(batch);
return result;
}
continue;
}
@ -2976,9 +2971,12 @@ static int sample_memory_bus_v1(struct target *target,
if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
/* Discard this batch when we encounter "busy error" state on the System Bus level.
* We'll try next time with a larger System Bus read delay. */
info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
dm_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
int res = riscv_scan_increase_delay(&info->learned_delays,
RISCV_DELAY_SYSBUS_READ);
riscv_batch_free(batch);
if (res != ERROR_OK)
return res;
continue;
}
if (get_field(sbcs_read, DM_SBCS_SBERROR)) {
@ -3135,11 +3133,7 @@ static int init_target(struct command_context *cmd_ctx,
riscv013_info_t *info = get_info(target);
info->progbufsize = -1;
info->dmi_busy_delay = 0;
info->bus_master_read_delay = 0;
info->bus_master_write_delay = 0;
info->ac_busy_delay = 0;
reset_learned_delays(target);
/* Assume all these abstract commands are supported until we learn
* otherwise.
@ -3226,7 +3220,8 @@ static int deassert_reset(struct target *target)
return result;
uint32_t dmstatus;
const int orig_dmi_busy_delay = info->dmi_busy_delay;
const unsigned int orig_base_delay = riscv_scan_get_delay(&info->learned_delays,
RISCV_DELAY_BASE);
time_t start = time(NULL);
LOG_TARGET_DEBUG(target, "Waiting for hart to come out of reset.");
do {
@ -3259,7 +3254,8 @@ static int deassert_reset(struct target *target)
} while (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL) &&
!get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET));
info->dmi_busy_delay = orig_dmi_busy_delay;
riscv_scan_set_delay(&info->learned_delays, RISCV_DELAY_BASE,
orig_base_delay);
if (target->reset_halt) {
target->state = TARGET_HALTED;
@ -3588,10 +3584,12 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
if (sb_write_address(target, next_address, true) != ERROR_OK)
return ERROR_FAIL;
if (info->bus_master_read_delay) {
unsigned int bus_master_read_delay = riscv_scan_get_delay(&info->learned_delays,
RISCV_DELAY_SYSBUS_READ);
if (bus_master_read_delay) {
LOG_TARGET_DEBUG(target, "Waiting %d cycles for bus master read delay",
info->bus_master_read_delay);
jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE);
bus_master_read_delay);
jtag_add_runtest(bus_master_read_delay, TAP_IDLE);
if (jtag_execute_queue() != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to scan idle sequence");
return ERROR_FAIL;
@ -3621,10 +3619,12 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
DMI_OP_READ, sbdata[j] + dm->base, 0, false);
/* By reading from sbdata0, we have just initiated another system bus read.
* If necessary add a delay so the read can finish. */
if (j == 0 && info->bus_master_read_delay) {
bus_master_read_delay = riscv_scan_get_delay(&info->learned_delays,
RISCV_DELAY_SYSBUS_READ);
if (j == 0 && bus_master_read_delay) {
LOG_TARGET_DEBUG(target, "Waiting %d cycles for bus master read delay",
info->bus_master_read_delay);
jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE);
bus_master_read_delay);
jtag_add_runtest(bus_master_read_delay, TAP_IDLE);
if (jtag_execute_queue() != ERROR_OK) {
LOG_TARGET_ERROR(target, "Failed to scan idle sequence");
return ERROR_FAIL;
@ -3711,9 +3711,10 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_FAIL;
}
info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
LOG_TARGET_DEBUG(target, "Increasing bus_master_read_delay to %d.",
info->bus_master_read_delay);
int res = riscv_scan_increase_delay(&info->learned_delays,
RISCV_DELAY_SYSBUS_READ);
if (res != ERROR_OK)
return res;
continue;
}
@ -4129,8 +4130,12 @@ static int read_memory_progbuf_inner_on_ac_busy(struct target *target,
uint32_t start_index, uint32_t *elements_read,
struct memory_access_info access)
{
increase_ac_busy_delay(target);
riscv013_clear_abstract_error(target);
int res = riscv013_clear_abstract_error(target);
if (res != ERROR_OK)
return res;
res = increase_ac_busy_delay(target);
if (res != ERROR_OK)
return res;
if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK)
return ERROR_FAIL;
@ -4828,8 +4833,11 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
LOG_TARGET_DEBUG(target, "Sbbusyerror encountered during system bus write.");
/* Clear the sticky error flag. */
dm_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR);
/* Slow down before trying again. */
info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1;
/* Slow down before trying again.
* FIXME: Possible overflow is ignored here.
*/
riscv_scan_increase_delay(&info->learned_delays,
RISCV_DELAY_SYSBUS_WRITE);
}
if (get_field(sbcs, DM_SBCS_SBBUSYERROR) || dmi_busy_encountered) {
@ -4940,8 +4948,12 @@ static int write_memory_progbuf_teardown(struct target *target)
static int write_memory_progbuf_handle_busy(struct target *target,
target_addr_t *address_p, uint32_t size, const uint8_t *buffer)
{
riscv013_clear_abstract_error(target);
increase_ac_busy_delay(target);
int res = riscv013_clear_abstract_error(target);
if (res != ERROR_OK)
return res;
res = increase_ac_busy_delay(target);
if (res != ERROR_OK)
return res;
if (write_memory_progbuf_teardown(target) != ERROR_OK)
return ERROR_FAIL;