Read idle, and test all debug RAM.
Read dtmcontrol's idle field to decide how many run-test/idle cycles are required to communicate with the target. In riscv_examine(), write and read all of Debug RAM to check the target is at least somewhat sane. Change-Id: Ieedb7a50418fa1f5e0d456cde53c52f7fc51662b__archive__
parent
c67850b63d
commit
b04d5e8821
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
#define DTMCONTROL 0x10
|
#define DTMCONTROL 0x10
|
||||||
#define DTMCONTROL_DBUS_RESET (1<<16)
|
#define DTMCONTROL_DBUS_RESET (1<<16)
|
||||||
|
#define DTMCONTROL_IDLE (7<<10)
|
||||||
#define DTMCONTROL_ADDRBITS (0xf<<4)
|
#define DTMCONTROL_ADDRBITS (0xf<<4)
|
||||||
#define DTMCONTROL_VERSION (0xf)
|
#define DTMCONTROL_VERSION (0xf)
|
||||||
|
|
||||||
|
@ -169,6 +170,10 @@ typedef struct {
|
||||||
// unique_id of the breakpoint/watchpoint that is using it.
|
// unique_id of the breakpoint/watchpoint that is using it.
|
||||||
int trigger_unique_id[MAX_HWBPS];
|
int trigger_unique_id[MAX_HWBPS];
|
||||||
|
|
||||||
|
// Number of run-test/idle cycles the target requests we do after each dbus
|
||||||
|
// access.
|
||||||
|
unsigned int dtmcontrol_idle;
|
||||||
|
|
||||||
// This value is incremented every time a dbus access comes back as "busy".
|
// 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
|
// It's used to determine how many run-test/idle cycles to feed the target
|
||||||
// in between accesses.
|
// in between accesses.
|
||||||
|
@ -208,10 +213,10 @@ static struct scan_field select_dbus = {
|
||||||
.in_value = NULL,
|
.in_value = NULL,
|
||||||
.out_value = ir_dbus
|
.out_value = ir_dbus
|
||||||
};
|
};
|
||||||
static uint8_t ir_debug[1] = {0x5};
|
static uint8_t ir_idcode[1] = {0x1};
|
||||||
static struct scan_field select_debug = {
|
static struct scan_field select_idcode = {
|
||||||
.in_value = NULL,
|
.in_value = NULL,
|
||||||
.out_value = ir_debug
|
.out_value = ir_idcode
|
||||||
};
|
};
|
||||||
#define DEBUG_LENGTH 264
|
#define DEBUG_LENGTH 264
|
||||||
|
|
||||||
|
@ -315,11 +320,40 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t idcode_scan(struct target *target)
|
||||||
|
{
|
||||||
|
struct scan_field field;
|
||||||
|
uint8_t in_value[4];
|
||||||
|
|
||||||
|
jtag_add_ir_scan(target->tap, &select_idcode, TAP_IDLE);
|
||||||
|
|
||||||
|
field.num_bits = 32;
|
||||||
|
field.out_value = NULL;
|
||||||
|
field.in_value = in_value;
|
||||||
|
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
|
||||||
|
|
||||||
|
int retval = jtag_execute_queue();
|
||||||
|
if (retval != ERROR_OK) {
|
||||||
|
LOG_ERROR("failed jtag scan: %d", retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Always return to dbus. */
|
||||||
|
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
|
||||||
|
|
||||||
|
uint32_t in = buf_get_u32(field.in_value, 0, 32);
|
||||||
|
LOG_DEBUG("IDCODE: 0x0 -> 0x%x", in);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
static void increase_dbus_busy_delay(struct target *target)
|
static void increase_dbus_busy_delay(struct target *target)
|
||||||
{
|
{
|
||||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||||
info->dbus_busy_delay++;
|
info->dbus_busy_delay++;
|
||||||
LOG_INFO("Increment dbus_busy_delay to %d", info->dbus_busy_delay);
|
LOG_INFO("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d",
|
||||||
|
info->dtmcontrol_idle, info->dbus_busy_delay,
|
||||||
|
info->interrupt_high_delay);
|
||||||
|
|
||||||
dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET);
|
dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET);
|
||||||
}
|
}
|
||||||
|
@ -328,7 +362,9 @@ static void increase_interrupt_high_delay(struct target *target)
|
||||||
{
|
{
|
||||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||||
info->interrupt_high_delay++;
|
info->interrupt_high_delay++;
|
||||||
LOG_INFO("Increment interrupt_high_delay to %d", info->interrupt_high_delay);
|
LOG_INFO("dtmcontrol_idle=%d, dbus_busy_delay=%d, interrupt_high_delay=%d",
|
||||||
|
info->dtmcontrol_idle, info->dbus_busy_delay,
|
||||||
|
info->interrupt_high_delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_dbus_scan(const struct target *target, struct scan_field *field,
|
static void add_dbus_scan(const struct target *target, struct scan_field *field,
|
||||||
|
@ -347,13 +383,14 @@ static void add_dbus_scan(const struct target *target, struct scan_field *field,
|
||||||
|
|
||||||
jtag_add_dr_scan(target->tap, 1, field, TAP_IDLE);
|
jtag_add_dr_scan(target->tap, 1, field, TAP_IDLE);
|
||||||
|
|
||||||
// TODO: 1 should come from the dtmcontrol register
|
int idle_count = info->dtmcontrol_idle + info->dbus_busy_delay;
|
||||||
int idle_count = 1 + info->dbus_busy_delay;
|
|
||||||
if (data & DMCONTROL_INTERRUPT) {
|
if (data & DMCONTROL_INTERRUPT) {
|
||||||
idle_count += info->interrupt_high_delay;
|
idle_count += info->interrupt_high_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
jtag_add_runtest(idle_count, TAP_IDLE);
|
if (idle_count) {
|
||||||
|
jtag_add_runtest(idle_count, TAP_IDLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_field(const struct scan_field *field)
|
static void dump_field(const struct scan_field *field)
|
||||||
|
@ -407,7 +444,9 @@ static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in,
|
||||||
|
|
||||||
/* Assume dbus is already selected. */
|
/* Assume dbus is already selected. */
|
||||||
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
|
jtag_add_dr_scan(target->tap, 1, &field, TAP_IDLE);
|
||||||
jtag_add_runtest(1, TAP_IDLE);
|
if (info->dtmcontrol_idle) {
|
||||||
|
jtag_add_runtest(info->dtmcontrol_idle, TAP_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
int retval = jtag_execute_queue();
|
int retval = jtag_execute_queue();
|
||||||
if (retval != ERROR_OK) {
|
if (retval != ERROR_OK) {
|
||||||
|
@ -670,7 +709,7 @@ static int dram_check32(struct target *target, unsigned int index,
|
||||||
static void cache_set32(struct target *target, unsigned int index, uint32_t data)
|
static void cache_set32(struct target *target, unsigned int index, uint32_t data)
|
||||||
{
|
{
|
||||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||||
if (false && info->dram_cache[index].valid &&
|
if (info->dram_cache[index].valid &&
|
||||||
info->dram_cache[index].data == data) {
|
info->dram_cache[index].data == data) {
|
||||||
// This is already preset on the target.
|
// This is already preset on the target.
|
||||||
LOG_DEBUG("cache[0x%x] = 0x%x (hit)", index, data);
|
LOG_DEBUG("cache[0x%x] = 0x%x (hit)", index, data);
|
||||||
|
@ -714,7 +753,7 @@ static void cache_set_store(struct target *target, unsigned int index,
|
||||||
|
|
||||||
static void dump_debug_ram(struct target *target)
|
static void dump_debug_ram(struct target *target)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < 16; i++) {
|
for (unsigned int i = 0; i < DRAM_CACHE_SIZE; i++) {
|
||||||
uint32_t value = dram_read32(target, i);
|
uint32_t value = dram_read32(target, i);
|
||||||
LOG_ERROR("Debug RAM 0x%x: 0x%08x", i, value);
|
LOG_ERROR("Debug RAM 0x%x: 0x%08x", i, value);
|
||||||
}
|
}
|
||||||
|
@ -843,7 +882,9 @@ static int cache_write(struct target *target, unsigned int address, bool run)
|
||||||
}
|
}
|
||||||
info->dram_cache[i].dirty = false;
|
info->dram_cache[i].dirty = false;
|
||||||
}
|
}
|
||||||
cache_clean(target);
|
if (run) {
|
||||||
|
cache_clean(target);
|
||||||
|
}
|
||||||
|
|
||||||
if (wait_for_debugint_clear(target, true) != ERROR_OK) {
|
if (wait_for_debugint_clear(target, true) != ERROR_OK) {
|
||||||
LOG_ERROR("Debug interrupt didn't clear.");
|
LOG_ERROR("Debug interrupt didn't clear.");
|
||||||
|
@ -852,7 +893,13 @@ static int cache_write(struct target *target, unsigned int address, bool run)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
cache_clean(target);
|
if (run) {
|
||||||
|
cache_clean(target);
|
||||||
|
} else {
|
||||||
|
for (unsigned int i = 0; i < info->dramsize; i++) {
|
||||||
|
info->dram_cache[i].dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (run || address < CACHE_NO_READ) {
|
if (run || address < CACHE_NO_READ) {
|
||||||
int interrupt = scans_get_u32(scans, scans->next_scan-1,
|
int interrupt = scans_get_u32(scans, scans->next_scan-1,
|
||||||
|
@ -1257,7 +1304,7 @@ static int riscv_init_target(struct command_context *cmd_ctx,
|
||||||
|
|
||||||
select_dtmcontrol.num_bits = target->tap->ir_length;
|
select_dtmcontrol.num_bits = target->tap->ir_length;
|
||||||
select_dbus.num_bits = target->tap->ir_length;
|
select_dbus.num_bits = target->tap->ir_length;
|
||||||
select_debug.num_bits = target->tap->ir_length;
|
select_idcode.num_bits = target->tap->ir_length;
|
||||||
|
|
||||||
const unsigned int max_reg_name_len = 12;
|
const unsigned int max_reg_name_len = 12;
|
||||||
info->reg_list = calloc(REG_COUNT, sizeof(struct reg));
|
info->reg_list = calloc(REG_COUNT, sizeof(struct reg));
|
||||||
|
@ -1633,8 +1680,7 @@ static int riscv_examine(struct target *target)
|
||||||
LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
|
LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
|
||||||
LOG_DEBUG(" addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS));
|
LOG_DEBUG(" addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS));
|
||||||
LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION));
|
LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION));
|
||||||
// TODO: Add support for the idle field, once it's implemented in the FPGA
|
LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTMCONTROL_IDLE));
|
||||||
// image.
|
|
||||||
if (dtmcontrol == 0) {
|
if (dtmcontrol == 0) {
|
||||||
LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
|
LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
|
@ -1647,6 +1693,13 @@ static int riscv_examine(struct target *target)
|
||||||
|
|
||||||
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
riscv_info_t *info = (riscv_info_t *) target->arch_info;
|
||||||
info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS);
|
info->addrbits = get_field(dtmcontrol, DTMCONTROL_ADDRBITS);
|
||||||
|
info->dtmcontrol_idle = get_field(dtmcontrol, DTMCONTROL_IDLE);
|
||||||
|
if (info->dtmcontrol_idle == 0) {
|
||||||
|
// Some old SiFive cores don't set idle but need it to be 1.
|
||||||
|
uint32_t idcode = idcode_scan(target);
|
||||||
|
if (idcode == 0x10e31913)
|
||||||
|
info->dtmcontrol_idle = 1;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t dminfo = dbus_read(target, DMINFO);
|
uint32_t dminfo = dbus_read(target, DMINFO);
|
||||||
LOG_DEBUG("dminfo: 0x%08x", dminfo);
|
LOG_DEBUG("dminfo: 0x%08x", dminfo);
|
||||||
|
@ -1677,7 +1730,7 @@ static int riscv_examine(struct target *target)
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out XLEN.
|
// Figure out XLEN, and test writing all of Debug RAM while we're at it.
|
||||||
cache_set32(target, 0, xori(S1, ZERO, -1));
|
cache_set32(target, 0, xori(S1, ZERO, -1));
|
||||||
// 0xffffffff 0xffffffff:ffffffff 0xffffffff:ffffffff:ffffffff:ffffffff
|
// 0xffffffff 0xffffffff:ffffffff 0xffffffff:ffffffff:ffffffff:ffffffff
|
||||||
cache_set32(target, 1, srli(S1, S1, 31));
|
cache_set32(target, 1, srli(S1, S1, 31));
|
||||||
|
@ -1687,6 +1740,9 @@ static int riscv_examine(struct target *target)
|
||||||
// 0x00000000 0x00000000:00000003 0x00000000:00000003:ffffffff:ffffffff
|
// 0x00000000 0x00000000:00000003 0x00000000:00000003:ffffffff:ffffffff
|
||||||
cache_set32(target, 4, sw(S1, ZERO, DEBUG_RAM_START + 4));
|
cache_set32(target, 4, sw(S1, ZERO, DEBUG_RAM_START + 4));
|
||||||
cache_set_jump(target, 5);
|
cache_set_jump(target, 5);
|
||||||
|
for (unsigned i = 6; i < info->dramsize; i++) {
|
||||||
|
cache_set32(target, i, i * 0x01020304);
|
||||||
|
}
|
||||||
|
|
||||||
cache_write(target, 0, false);
|
cache_write(target, 0, false);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue