Quickly read all GPRs on halt.

gdb will ask for them anyway, and one by one is slow.
StepTest went from 9.7s to 5.3s.
__archive__
Tim Newsome 2016-06-23 13:42:18 -07:00
parent 6fac5a41f8
commit 20e2bfe3db
1 changed files with 272 additions and 101 deletions

View File

@ -24,6 +24,8 @@
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
#define DIM(x) (sizeof(x)/sizeof(*x))
#define CSR_TDRSELECT 0x7a0
#define CSR_TDRDATA1 0x7a1
#define CSR_TDRDATA2 0x7a2
@ -71,6 +73,12 @@ typedef enum {
#define DBUS_DATA_SIZE 34
#define DBUS_ADDRESS_START 36
typedef enum {
RE_OK,
RE_FAIL,
RE_AGAIN
} riscv_error_t;
/*** Debug Bus registers. ***/
#define DMCONTROL 0x10
@ -151,13 +159,16 @@ typedef struct {
// 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 dbus_busy_count;
unsigned int dbus_busy_delay;
// This value is incremented every time we read the debug interrupt as
// high. It's used to add extra run-test/idle cycles after setting debug
// interrupt high, so ideally we never have to perform a whole extra scan
// before the interrupt is cleared.
unsigned int interrupt_high_count;
unsigned int interrupt_high_delay;
// This cache is write-through, and always valid when the target is halted.
uint32_t gpr_cache[32];
} riscv_info_t;
typedef struct {
@ -228,6 +239,47 @@ static int debug_scan(struct target *target)
}
#endif
static void increase_dbus_busy_delay(struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
info->dbus_busy_delay++;
LOG_INFO("Increment dbus_busy_delay to %d", info->dbus_busy_delay);
}
static void increase_interrupt_high_delay(struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
info->interrupt_high_delay++;
LOG_INFO("Increment interrupt_high_delay to %d", info->interrupt_high_delay);
}
static void add_dbus_scan(struct target *target, struct scan_field *field,
uint8_t *out_value, uint8_t *in_value, dbus_op_t op, uint16_t address,
uint64_t data)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
LOG_DEBUG("op=%d address=0x%02x data=0x%09" PRIx64, op, address, data);
field->num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
field->in_value = in_value;
field->out_value = out_value;
buf_set_u64(out_value, DBUS_OP_START, DBUS_OP_SIZE, op);
buf_set_u64(out_value, DBUS_DATA_START, DBUS_DATA_SIZE, data);
buf_set_u64(out_value, DBUS_ADDRESS_START, info->addrbits, address);
jtag_add_dr_scan(target->tap, 1, field, TAP_IDLE);
// TODO: 1 should come from the dtminfo register
int idle_count = 1 + info->dbus_busy_delay;
if (data & DMCONTROL_INTERRUPT) {
idle_count += info->interrupt_high_delay;
}
jtag_add_runtest(idle_count, TAP_IDLE);
}
static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in,
uint64_t *data_in, dbus_op_t op, uint16_t address_out, uint64_t data_out)
{
@ -477,28 +529,12 @@ static int cache_write(struct target *target, unsigned int address, bool run)
} else {
for (unsigned int i = 0; i < DRAM_CACHE_SIZE; i++) {
if (info->dram_cache[i].dirty) {
field[scan].num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
field[scan].out_value = out + 8*scan;
field[scan].in_value = in + 8*scan;
buf_set_u64(out + 8*scan, DBUS_OP_START, DBUS_OP_SIZE, DBUS_OP_WRITE);
uint64_t data = DMCONTROL_HALTNOT | info->dram_cache[i].data;
if (i == last && run) {
buf_set_u64(out + 8*scan, DBUS_DATA_START, DBUS_DATA_SIZE,
DMCONTROL_INTERRUPT | DMCONTROL_HALTNOT | info->dram_cache[i].data);
} else {
buf_set_u64(out + 8*scan, DBUS_DATA_START, DBUS_DATA_SIZE,
DMCONTROL_HALTNOT | info->dram_cache[i].data);
data |= DMCONTROL_INTERRUPT;
}
buf_set_u64(out + 8*scan, DBUS_ADDRESS_START, info->addrbits, i);
jtag_add_dr_scan(target->tap, 1, &field[scan], TAP_IDLE);
jtag_add_runtest(1 + info->dbus_busy_count, TAP_IDLE);
LOG_DEBUG("write scan=%d result=%d data=%09" PRIx64 " address=%02x",
scan,
buf_get_u32(out + 8*scan, DBUS_OP_START, DBUS_OP_SIZE),
buf_get_u64(out + 8*scan, DBUS_DATA_START, DBUS_DATA_SIZE),
buf_get_u32(out + 8*scan, DBUS_ADDRESS_START, info->addrbits));
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_WRITE, i, data);
scan++;
}
@ -507,34 +543,16 @@ static int cache_write(struct target *target, unsigned int address, bool run)
// Throw away the results of the first read, since it'll contain the result
// of the read that happened just before debugint was set.
field[scan].num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
field[scan].out_value = out + 8*scan;
field[scan].in_value = NULL;
buf_set_u64(out + 8*scan, DBUS_OP_START, DBUS_OP_SIZE, DBUS_OP_READ);
buf_set_u64(out + 8*scan, DBUS_DATA_START, DBUS_DATA_SIZE, DMCONTROL_HALTNOT | 0);
buf_set_u64(out + 8*scan, DBUS_ADDRESS_START, info->addrbits, address);
jtag_add_dr_scan(target->tap, 1, &field[scan], TAP_IDLE);
jtag_add_runtest(1 + info->dbus_busy_count + info->interrupt_high_count, TAP_IDLE);
add_dbus_scan(target, &field[scan], out + 8*scan, NULL, DBUS_OP_READ,
address, DMCONTROL_HALTNOT);
scan++;
// This scan contains the results of the read the caller requested, as well
// as an interrupt bit worth looking at.
field[scan].num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
field[scan].out_value = out + 8*scan;
field[scan].in_value = in + 8*scan;
buf_set_u64(out + 8*scan, DBUS_OP_START, DBUS_OP_SIZE, DBUS_OP_READ);
buf_set_u64(out + 8*scan, DBUS_DATA_START, DBUS_DATA_SIZE, DMCONTROL_HALTNOT | 0);
buf_set_u64(out + 8*scan, DBUS_ADDRESS_START, info->addrbits, address);
jtag_add_dr_scan(target->tap, 1, &field[scan], TAP_IDLE);
jtag_add_runtest(1 + info->dbus_busy_count, TAP_IDLE);
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan, DBUS_OP_READ,
address, DMCONTROL_HALTNOT);
scan++;
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("JTAG execute failed.");
@ -565,8 +583,7 @@ static int cache_write(struct target *target, unsigned int address, bool run)
}
if (errors) {
LOG_INFO("Target couldn't handle our write speed. Slowing down...");
info->dbus_busy_count++;
increase_dbus_busy_delay(target);
// Try again, using the slow careful code.
for (unsigned int i = 0; i < DRAM_CACHE_SIZE; i++) {
@ -590,7 +607,7 @@ static int cache_write(struct target *target, unsigned int address, bool run)
int interrupt = buf_get_u32(in + 8*(scan-1), DBUS_DATA_START + 33, 1);
if (interrupt) {
info->interrupt_high_count++;
increase_interrupt_high_delay(target);
// Slow path wait for it to clear.
if (wait_for_debugint_clear(target, false) != ERROR_OK) {
LOG_ERROR("Debug interrupt didn't clear.");
@ -783,6 +800,9 @@ static int resume(struct target *target, int current, uint32_t address,
}
target->state = TARGET_RUNNING;
for (unsigned int i = 0; i < 32; i++) {
info->gpr_cache[i] = 0xbadbad;
}
return ERROR_OK;
}
@ -814,21 +834,10 @@ static int register_get(struct reg *reg)
struct target *target = (struct target *) reg->arch_info;
riscv_info_t *info = (riscv_info_t *) target->arch_info;
if (reg->number == S0) {
cache_set(target, 0, csrr(S0, CSR_DSCRATCH));
cache_set(target, 1, sw(S0, ZERO, DEBUG_RAM_START + 16));
cache_set_jump(target, 2);
} else if (reg->number == S1) {
cache_set(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 4 * info->dramsize - 4));
cache_set(target, 1, sw(S0, ZERO, DEBUG_RAM_START + 16));
cache_set_jump(target, 2);
} else if (reg->number == ZERO) {
buf_set_u64(reg->value, 0, info->xlen, 0);
LOG_DEBUG("%s=0x%x", reg->name, 0);
if (reg->number <= REG_XPR31) {
buf_set_u64(reg->value, 0, info->xlen, info->gpr_cache[reg->number]);
LOG_DEBUG("%s=0x%x", reg->name, info->gpr_cache[reg->number]);
return ERROR_OK;
} else if (reg->number <= REG_XPR31) {
cache_set(target, 0, sw(reg->number - REG_XPR0, ZERO, DEBUG_RAM_START + 16));
cache_set_jump(target, 1);
} else if (reg->number == REG_PC) {
buf_set_u32(reg->value, 0, 32, info->dpc);
LOG_DEBUG("%s=0x%x (cached)", reg->name, info->dpc);
@ -850,6 +859,11 @@ static int register_get(struct reg *reg)
}
uint32_t value = cache_get32(target, 4);
if (reg->number < 32 && info->gpr_cache[reg->number] != value) {
LOG_ERROR("cached value for %s is 0x%x but just read 0x%x",
reg->name, info->gpr_cache[reg->number], value);
assert(info->gpr_cache[reg->number] == value);
}
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
@ -906,10 +920,12 @@ static int register_write(struct target *target, unsigned int number,
static int register_set(struct reg *reg, uint8_t *buf)
{
struct target *target = (struct target *) reg->arch_info;
riscv_info_t *info = (riscv_info_t *) target->arch_info;
uint32_t value = buf_get_u32(buf, 0, 32);
LOG_DEBUG("write 0x%x to %s", value, reg->name);
info->gpr_cache[reg->number] = value;
return register_write(target, reg->number, value);
}
@ -1210,18 +1226,202 @@ static int riscv_examine(struct target *target)
return ERROR_OK;
}
static riscv_error_t handle_halt_routine(struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
const unsigned int max_scan = 256;
uint8_t *in = malloc(max_scan * 8);
uint8_t *out = malloc(max_scan * 8);
struct scan_field *field = calloc(max_scan, sizeof(struct scan_field));
unsigned int scan = 0;
// Read all GPRs as fast as we can, because gdb is going to ask for them
// anyway. Reading them one at a time is much slower.
// TODO
// Write the jump back to address 1.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_WRITE, 1, DMCONTROL_HALTNOT |
jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*1))));
scan++;
for (int reg = 1; reg < 32; reg++) {
if (reg == S0 || reg == S1) {
continue;
}
// Write store instruction.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_WRITE, 0, DMCONTROL_INTERRUPT | DMCONTROL_HALTNOT |
sw(reg, ZERO, DEBUG_RAM_START + 16));
scan++;
// Read value.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_READ, 4, DMCONTROL_HALTNOT);
scan++;
assert(scan < max_scan);
}
// Write store of s0 at index 1.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_WRITE, 1, DMCONTROL_HALTNOT |
sw(S0, ZERO, DEBUG_RAM_START + 16));
scan++;
// Write jump at index 2.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_WRITE, 2, DMCONTROL_HALTNOT |
jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*2))));
cache_set(target, 0, csrr(S0, CSR_DSCRATCH));
scan++;
// Read S1 from debug RAM
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_WRITE, 0, DMCONTROL_INTERRUPT | DMCONTROL_HALTNOT |
lw(S0, ZERO, DEBUG_RAM_START + 4 * info->dramsize - 4));
scan++;
// Read value.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_READ, 4, DMCONTROL_HALTNOT);
scan++;
// Read S0 from dscratch
unsigned int csr[] = {CSR_DSCRATCH, CSR_DPC, CSR_DCSR};
for (unsigned int i = 0; i < DIM(csr); i++) {
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_WRITE, 0, DMCONTROL_INTERRUPT | DMCONTROL_HALTNOT |
csrr(S0, csr[i]));
scan++;
// Read value.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_READ, 4, DMCONTROL_HALTNOT);
scan++;
assert(scan < max_scan);
}
// Final read to get the last value out.
add_dbus_scan(target, &field[scan], out + 8*scan, in + 8*scan,
DBUS_OP_READ, 4, DMCONTROL_HALTNOT);
scan++;
assert(scan < max_scan);
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
LOG_ERROR("JTAG execute failed: %d", retval);
goto error;
}
unsigned int dbus_busy = 0;
unsigned int interrupt_set = 0;
unsigned result = 0;
info->gpr_cache[0] = 0;
// The first scan result is the result from something old we don't care
// about.
for (unsigned int i = 1; i < scan && dbus_busy == 0; i++) {
dbus_status_t status = buf_get_u32(in + 8*i, DBUS_OP_START, DBUS_OP_SIZE);
uint64_t data = buf_get_u64(in + 8*i, DBUS_DATA_START, DBUS_DATA_SIZE);
uint32_t address = buf_get_u32(in + 8*i, DBUS_ADDRESS_START, info->addrbits);
LOG_DEBUG("read scan=%d result=%d data=%09" PRIx64 " address=%02x",
i, status, data, address);
switch (status) {
case DBUS_STATUS_SUCCESS:
break;
case DBUS_STATUS_NO_WRITE:
LOG_ERROR("Got no-write response without conditional write. Hardware error?");
goto error;
case DBUS_STATUS_FAILED:
LOG_ERROR("Debug access failed. Hardware error?");
goto error;
case DBUS_STATUS_BUSY:
dbus_busy++;
break;
}
if (data & DMCONTROL_INTERRUPT) {
interrupt_set++;
break;
}
if (address == 4) {
switch (result) {
case 0: info->gpr_cache[1] = data; break;
case 1: info->gpr_cache[2] = data; break;
case 2: info->gpr_cache[3] = data; break;
case 3: info->gpr_cache[4] = data; break;
case 4: info->gpr_cache[5] = data; break;
case 5: info->gpr_cache[6] = data; break;
case 6: info->gpr_cache[7] = data; break;
// S0
// S1
case 7: info->gpr_cache[10] = data; break;
case 8: info->gpr_cache[11] = data; break;
case 9: info->gpr_cache[12] = data; break;
case 10: info->gpr_cache[13] = data; break;
case 11: info->gpr_cache[14] = data; break;
case 12: info->gpr_cache[15] = data; break;
case 13: info->gpr_cache[16] = data; break;
case 14: info->gpr_cache[17] = data; break;
case 15: info->gpr_cache[18] = data; break;
case 16: info->gpr_cache[19] = data; break;
case 17: info->gpr_cache[20] = data; break;
case 18: info->gpr_cache[21] = data; break;
case 19: info->gpr_cache[22] = data; break;
case 20: info->gpr_cache[23] = data; break;
case 21: info->gpr_cache[24] = data; break;
case 22: info->gpr_cache[25] = data; break;
case 23: info->gpr_cache[26] = data; break;
case 24: info->gpr_cache[27] = data; break;
case 25: info->gpr_cache[28] = data; break;
case 26: info->gpr_cache[29] = data; break;
case 27: info->gpr_cache[30] = data; break;
case 28: info->gpr_cache[31] = data; break;
case 29: info->gpr_cache[S1] = data; break;
case 30: info->gpr_cache[S0] = data; break;
case 31: info->dpc = data; break;
case 32: info->dcsr = data; break;
default:
assert(0);
}
result++;
}
}
free(in);
free(out);
free(field);
cache_invalidate(target);
if (dbus_busy) {
increase_dbus_busy_delay(target);
return RE_AGAIN;
}
if (interrupt_set) {
increase_interrupt_high_delay(target);
return RE_AGAIN;
}
return RE_OK;
error:
free(in);
free(out);
free(field);
return RE_FAIL;
}
static int handle_halt(struct target *target)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
target->state = TARGET_HALTED;
if (read_csr(target, &info->dpc, CSR_DPC) != ERROR_OK) {
riscv_error_t re;
do {
re = handle_halt_routine(target);
} while (re == RE_AGAIN);
if (re != RE_OK) {
LOG_ERROR("handle_halt_routine failed");
return ERROR_FAIL;
}
if (read_csr(target, &info->dcsr, CSR_DCSR) != ERROR_OK) {
return ERROR_FAIL;
}
int cause = get_field(info->dcsr, DCSR_CAUSE);
LOG_DEBUG("halt cause is %d; dcsr=0x%x", cause, info->dcsr);
switch (cause) {
@ -1440,33 +1640,6 @@ static int riscv_read_memory(struct target *target, uint32_t address,
return ERROR_OK;
}
static void add_dbus_scan(struct target *target, struct scan_field *field,
uint8_t *out_value, uint8_t *in_value, dbus_op_t op, uint16_t address,
uint64_t data)
{
riscv_info_t *info = (riscv_info_t *) target->arch_info;
LOG_DEBUG("op=%d address=0x%02x data=0x%09" PRIx64, op, address, data);
field->num_bits = info->addrbits + DBUS_OP_SIZE + DBUS_DATA_SIZE;
field->in_value = in_value;
field->out_value = out_value;
buf_set_u64(out_value, DBUS_OP_START, DBUS_OP_SIZE, op);
buf_set_u64(out_value, DBUS_DATA_START, DBUS_DATA_SIZE, data);
buf_set_u64(out_value, DBUS_ADDRESS_START, info->addrbits, address);
jtag_add_dr_scan(target->tap, 1, field, TAP_IDLE);
// TODO: 1 should come from the dtminfo register
int idle_count = 1 + info->dbus_busy_count;
if (data & DMCONTROL_INTERRUPT) {
idle_count += info->interrupt_high_count;
}
jtag_add_runtest(idle_count, TAP_IDLE);
}
#if 1
static int setup_write_memory(struct target *target, uint32_t size)
{
@ -1515,14 +1688,14 @@ static int riscv_write_memory(struct target *target, uint32_t address,
return ERROR_FAIL;
}
#define MAX_BATCH_SIZE 256
uint8_t *in = malloc(MAX_BATCH_SIZE * 8);
uint8_t *out = malloc(MAX_BATCH_SIZE * 8);
struct scan_field *field = calloc(MAX_BATCH_SIZE, sizeof(struct scan_field));
const int max_batch_size = 256;
uint8_t *in = malloc(max_batch_size * 8);
uint8_t *out = malloc(max_batch_size * 8);
struct scan_field *field = calloc(max_batch_size, sizeof(struct scan_field));
uint32_t i = 0;
while (i < count + 1) {
unsigned int batch_size = MIN(count + 1 - i, MAX_BATCH_SIZE);
unsigned int batch_size = MIN(count + 1 - i, max_batch_size);
for (unsigned int j = 0; j < batch_size; j++) {
if (i + j == count) {
@ -1584,12 +1757,10 @@ static int riscv_write_memory(struct target *target, uint32_t address,
LOG_DEBUG("j=%d data=%09" PRIx64, j, buf_get_u64(in + 8*j, DBUS_DATA_START, DBUS_DATA_SIZE));
}
if (dbus_busy) {
info->dbus_busy_count++;
LOG_INFO("Increment dbus_busy_count to %d", info->dbus_busy_count);
increase_dbus_busy_delay(target);
}
if (execute_busy) {
info->interrupt_high_count++;
LOG_INFO("Increment interrupt_high_count to %d", info->interrupt_high_count);
increase_interrupt_high_delay(target);
}
if (dbus_busy || execute_busy) {
wait_for_debugint_clear(target, false);