Some memory access works.

MemTest16 passes, but MemTest32 fails.

Change-Id: I17fbc38b4228b27c7fb3dadb15e9c1a2f67bcd65
This commit is contained in:
Tim Newsome 2017-02-15 15:44:36 -08:00
parent 657e844c8c
commit 79e840aaa7
2 changed files with 167 additions and 357 deletions

View File

@ -28,11 +28,11 @@
#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1
#define AC_ACCESS_REGISTER_POSTEXEC (0x1 << AC_ACCESS_REGISTER_POSTEXEC_OFFSET)
/*
* 0: Copy data from \Rdatazero into the specified register.
* 0: Copy data from {\tt arg0} portion of {\tt data} into the
* specified register.
*
* 1: Copy data from the specified register into \Rdatazero.
*
* (If XLEN is greater than 32, more {\tt data} registers are involved.)
* 1: Copy data from the specified register into {\tt arg0} portion
* of {\tt data}.
*/
#define AC_ACCESS_REGISTER_WRITE_OFFSET 16
#define AC_ACCESS_REGISTER_WRITE_LENGTH 1
@ -169,37 +169,25 @@
#define CSR_PRIV_PRV (0x3 << CSR_PRIV_PRV_OFFSET)
#define DMI_DMCONTROL 0x00
/*
* Halt request signal for the hart selected by \Fhartsel. Writes
* apply to the new value of \Fhartsel.
* Halt request signal for the hart selected by \Fhartsel. When 1, the
* hart will halt if it's not currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
*
* Writes apply to the new value of \Fhartsel.
*/
#define DMI_DMCONTROL_HALTREQ_OFFSET 31
#define DMI_DMCONTROL_HALTREQ_LENGTH 1
#define DMI_DMCONTROL_HALTREQ (0x1 << DMI_DMCONTROL_HALTREQ_OFFSET)
/*
* This bit controls the reset signal from the DM to the rest of the
* system. To perform a reset the debugger writes 1, and then writes 0
* to deassert the reset.
*/
#define DMI_DMCONTROL_RESET_OFFSET 30
#define DMI_DMCONTROL_RESET_LENGTH 1
#define DMI_DMCONTROL_RESET (0x1 << DMI_DMCONTROL_RESET_OFFSET)
/*
* This bit serves as a reset signal for the Debug Module itself.
* When 0, the module is held in reset. When 1, it functions normally.
* No other mechanism should exist that may result in resetting the
* Debug Module after power up, including the platform's system reset
* or Debug Transport reset signals.
* Resume request signal for the hart selected by \Fhartsel. When 1,
* the hart will resume if it's currently halted.
* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
*
* A debugger should pulse this bit low to ensure that the Debug
* Module is fully reset and ready to use.
*
* Implementations may use this bit to aid debugging, for example by
* preventing the Debug Module from being power gated while debugging
* is active.
* Writes apply to the new value of \Fhartsel.
*/
#define DMI_DMCONTROL_DMACTIVE_OFFSET 29
#define DMI_DMCONTROL_DMACTIVE_LENGTH 1
#define DMI_DMCONTROL_DMACTIVE (0x1 << DMI_DMCONTROL_DMACTIVE_OFFSET)
#define DMI_DMCONTROL_RESUMEREQ_OFFSET 30
#define DMI_DMCONTROL_RESUMEREQ_LENGTH 1
#define DMI_DMCONTROL_RESUMEREQ (0x1 << DMI_DMCONTROL_RESUMEREQ_OFFSET)
/*
* The status of the currently selected hart.
*
@ -221,6 +209,31 @@
#define DMI_DMCONTROL_HARTSEL_LENGTH 10
#define DMI_DMCONTROL_HARTSEL (0x3ff << DMI_DMCONTROL_HARTSEL_OFFSET)
/*
* This bit serves as a reset signal for the Debug Module itself.
* When 0, the module is held in reset. When 1, it functions normally.
* No other mechanism should exist that may result in resetting the
* Debug Module after power up, including the platform's system reset
* or Debug Transport reset signals.
*
* A debugger should pulse this bit low to ensure that the Debug
* Module is fully reset and ready to use.
*
* Implementations may use this bit to aid debugging, for example by
* preventing the Debug Module from being power gated while debugging
* is active.
*/
#define DMI_DMCONTROL_DMACTIVE_OFFSET 9
#define DMI_DMCONTROL_DMACTIVE_LENGTH 1
#define DMI_DMCONTROL_DMACTIVE (0x1 << DMI_DMCONTROL_DMACTIVE_OFFSET)
/*
* This bit controls the reset signal from the DM to the rest of the
* system. To perform a reset the debugger writes 1, and then writes 0
* to deassert the reset.
*/
#define DMI_DMCONTROL_RESET_OFFSET 8
#define DMI_DMCONTROL_RESET_LENGTH 1
#define DMI_DMCONTROL_RESET (0x1 << DMI_DMCONTROL_RESET_OFFSET)
/*
* 0 when authentication is required before using the DM. 1 when the
* authentication check has passed. On components that don't implement
* authentication, this bit must be preset as 1.
@ -450,8 +463,10 @@
*
* 3: There was some other error (eg. alignment).
*
* 4: The system bus master was busy when a one of the {\tt sbaddress} or
* {\tt sbdata} registers was written.
* 4: The system bus master was busy when a one of the
* {\tt sbaddress} or {\tt sbdata} registers was written,
* or the {\tt sbdata} register was read when it had
* stale data.
*/
#define DMI_SBCS_SBERROR_OFFSET 12
#define DMI_SBCS_SBERROR_LENGTH 3

View File

@ -392,7 +392,7 @@ static void add_dmi_scan(const struct target *target, struct scan_field *field,
static void dump_field(const struct scan_field *field)
{
static const char *op_string[] = {"nop", "r", "w", "?"};
static const char *op_string[] = {"-", "r", "w", "?"};
static const char *status_string[] = {"+", "?", "F", "b"};
if (debug_level < LOG_LVL_DEBUG)
@ -542,11 +542,6 @@ static scans_t *scans_delete(scans_t *scans)
return NULL;
}
static void scans_reset(scans_t *scans)
{
scans->next_scan = 0;
}
static void scans_dump(scans_t *scans)
{
for (unsigned int i = 0; i < scans->next_scan; i++) {
@ -598,12 +593,6 @@ static uint32_t scans_get_u32(scans_t *scans, unsigned int index,
return buf_get_u32(scans->in + scans->scan_size * index, first, num);
}
static uint64_t scans_get_u64(scans_t *scans, unsigned int index,
unsigned first, unsigned num)
{
return buf_get_u64(scans->in + scans->scan_size * index, first, num);
}
/*** end of scans class ***/
/** Convert register number (internal OpenOCD number) to the number expected by
@ -784,6 +773,22 @@ static bits_t read_bits(struct target *target)
return result;
}
static int wait_for_haltstatus(struct target *target, unsigned status)
{
time_t start = time(NULL);
while (1) {
uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
unsigned s = get_field(dmcontrol, DMI_DMCONTROL_HARTSTATUS);
if (s == status)
return ERROR_OK;
if (time(NULL) - start > WALL_CLOCK_TIMEOUT) {
LOG_ERROR("Timed out waiting for hart status to be %d (dmcontrol=0x%x)",
status, dmcontrol);
return ERROR_FAIL;
}
}
}
static int wait_for_debugint_clear(struct target *target, bool ignore_first)
{
time_t start = time(NULL);
@ -1020,16 +1025,6 @@ static uint32_t cache_get32(struct target *target, unsigned int address)
return info->dram_cache[address].data;
}
static uint64_t cache_get(struct target *target, slot_t slot)
{
unsigned int offset = slot_offset(target, slot);
uint64_t value = cache_get32(target, offset);
if (xlen(target) > 32) {
value |= ((uint64_t) cache_get32(target, offset + 1)) << 32;
}
return value;
}
/* Write instruction that jumps from the specified word in Debug RAM to resume
* in Debug ROM. */
static void dram_write_jump(struct target *target, unsigned int index,
@ -1058,7 +1053,7 @@ static int wait_for_state(struct target *target, enum target_state state)
}
}
static int execute_program(struct target *target, const program_t *program)
static void write_program(struct target *target, const program_t *program)
{
riscv013_info_t *info = get_info(target);
@ -1071,6 +1066,11 @@ static int execute_program(struct target *target, const program_t *program)
((uint32_t) program->code[i+3] << 24);
dmi_write(target, DMI_IBUF0 + i / 4, value);
}
}
static int execute_program(struct target *target, const program_t *program)
{
write_program(target, program);
uint32_t command = 0;
if (program->write) {
@ -1188,17 +1188,6 @@ static int write_csr(struct target *target, uint32_t csr, uint64_t value)
return result;
}
static int write_gpr(struct target *target, unsigned int gpr, uint64_t value)
{
cache_set_load(target, 0, gpr, SLOT0);
cache_set_jump(target, 1);
cache_set(target, SLOT0, value);
if (cache_write(target, 4, true) != ERROR_OK) {
return ERROR_FAIL;
}
return ERROR_OK;
}
static int maybe_read_tselect(struct target *target)
{
riscv013_info_t *info = get_info(target);
@ -1237,11 +1226,7 @@ static int execute_resume(struct target *target, bool step)
// TODO: check if dpc is dirty (which also is true if an exception was hit
// at any time)
cache_set_load(target, 0, S0, SLOT0);
cache_set32(target, 1, csrw(S0, CSR_DPC));
cache_set_jump(target, 2);
cache_set(target, SLOT0, info->dpc);
if (cache_write(target, 4, true) != ERROR_OK) {
if (write_csr(target, CSR_DPC, info->dpc) != ERROR_OK) {
return ERROR_FAIL;
}
@ -1249,11 +1234,7 @@ static int execute_resume(struct target *target, bool step)
if (mstatus_reg->valid) {
uint64_t mstatus_user = buf_get_u64(mstatus_reg->value, 0, xlen(target));
if (mstatus_user != info->mstatus_actual) {
cache_set_load(target, 0, S0, SLOT0);
cache_set32(target, 1, csrw(S0, CSR_MSTATUS));
cache_set_jump(target, 2);
cache_set(target, SLOT0, mstatus_user);
if (cache_write(target, 4, true) != ERROR_OK) {
if (write_csr(target, CSR_MSTATUS, mstatus_user) != ERROR_OK) {
return ERROR_FAIL;
}
}
@ -1268,19 +1249,22 @@ static int execute_resume(struct target *target, bool step)
info->dcsr &= ~DCSR_STEP;
}
dram_write32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16), false);
dram_write32(target, 1, csrw(S0, CSR_DCSR), false);
dram_write32(target, 2, fence_i(), false);
dram_write_jump(target, 3, false);
if (write_csr(target, CSR_DCSR, info->dcsr) != ERROR_OK) {
return ERROR_FAIL;
}
// Write DCSR value, set interrupt and clear haltnot.
uint64_t dmi_value = DMCONTROL_INTERRUPT | info->dcsr;
dmi_write(target, dram_address(4), dmi_value);
program_t *program = program_new();
program_add32(program, fence_i());
program_add32(program, ebreak());
if (execute_program(target, program) != ERROR_OK) {
return ERROR_FAIL;
}
program_delete(program);
cache_invalidate(target);
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE |
DMI_DMCONTROL_RESUMEREQ);
if (wait_for_debugint_clear(target, true) != ERROR_OK) {
LOG_ERROR("Debug interrupt didn't clear.");
if (wait_for_haltstatus(target, 1) != ERROR_OK) {
return ERROR_FAIL;
}
@ -1942,9 +1926,8 @@ static int examine(struct target *target)
}
// Reset the Debug Module.
dmi_write(target, DMI_DMCONTROL, dmcontrol & DMI_DMCONTROL_HALTREQ);
dmi_write(target, DMI_DMCONTROL, (dmcontrol & DMI_DMCONTROL_HALTREQ) |
DMI_DMCONTROL_DMACTIVE);
dmi_write(target, DMI_DMCONTROL, 0);
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
dmcontrol = dmi_read(target, DMI_DMCONTROL);
LOG_DEBUG("dmcontrol: 0x%08x", dmcontrol);
@ -2063,7 +2046,8 @@ static int examine(struct target *target)
if (hartstatus == 1) {
// Resume if the hart had been running.
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE |
DMI_DMCONTROL_RESUMEREQ);
for (unsigned i = 0; i < 256; i++) {
dmcontrol = dmi_read(target, DMI_DMCONTROL);
if (get_field(dmcontrol, DMI_DMCONTROL_HARTSTATUS) == 1)
@ -2320,162 +2304,55 @@ static int read_memory(struct target *target, uint32_t address,
{
select_dmi(target);
cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16));
abstract_write_register(target, S0, xlen(target), address);
program_t *program = program_new();
switch (size) {
case 1:
cache_set32(target, 1, lb(S1, S0, 0));
cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
program_add32(program, lb(S1, S0, 0));
break;
case 2:
cache_set32(target, 1, lh(S1, S0, 0));
cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
program_add32(program, lh(S1, S0, 0));
break;
case 4:
cache_set32(target, 1, lw(S1, S0, 0));
cache_set32(target, 2, sw(S1, ZERO, DEBUG_RAM_START + 16));
program_add32(program, lw(S1, S0, 0));
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
}
cache_set_jump(target, 3);
cache_write(target, CACHE_NO_READ, false);
program_add32(program, addi(S0, S0, size));
program_add32(program, ebreak());
write_program(target, program);
program_delete(program);
riscv013_info_t *info = get_info(target);
const int max_batch_size = 256;
scans_t *scans = scans_new(target, max_batch_size);
execute_abstract_command(target,
AC_ACCESS_REGISTER_PREEXEC |
abstract_register_size(xlen(target)) | reg_number_to_no(S1));
dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_AUTOEXEC0);
uint32_t result_value = 0x777;
uint32_t i = 0;
while (i < count + 3) {
unsigned int batch_size = MIN(count + 3 - i, max_batch_size);
scans_reset(scans);
for (unsigned int j = 0; j < batch_size; j++) {
if (i + j == count) {
// Just insert a read so we can scan out the last value.
scans_add_read32(scans, 4, false);
} else if (i + j >= count + 1) {
// And check for errors.
scans_add_read32(scans, info->dramsize-1, false);
} else {
// Write the next address and set interrupt.
uint32_t offset = size * (i + j);
scans_add_write32(scans, 4, address + offset, true);
}
}
int retval = scans_execute(scans);
if (retval != ERROR_OK) {
LOG_ERROR("JTAG execute failed: %d", retval);
goto error;
}
int dmi_busy = 0;
int execute_busy = 0;
for (unsigned int j = 0; j < batch_size; j++) {
dmi_status_t status = scans_get_u32(scans, j, DMI_OP_START,
DMI_OP_SIZE);
switch (status) {
case DMI_STATUS_SUCCESS:
break;
case DMI_STATUS_FAILED:
LOG_ERROR("Debug RAM write failed. Hardware error?");
goto error;
case DMI_STATUS_BUSY:
dmi_busy++;
break;
default:
LOG_ERROR("Got invalid bus access status: %d", status);
return ERROR_FAIL;
}
uint64_t data = scans_get_u64(scans, j, DMI_DATA_START,
DMI_DATA_SIZE);
if (data & DMCONTROL_INTERRUPT) {
execute_busy++;
}
if (i + j == count + 2) {
result_value = data;
} else if (i + j > 1) {
uint32_t offset = size * (i + j - 2);
switch (size) {
case 1:
buffer[offset] = data;
break;
case 2:
buffer[offset] = data;
buffer[offset+1] = data >> 8;
break;
case 4:
buffer[offset] = data;
buffer[offset+1] = data >> 8;
buffer[offset+2] = data >> 16;
buffer[offset+3] = data >> 24;
break;
}
}
LOG_DEBUG("j=%d status=%d data=%09" PRIx64, j, status, data);
}
if (dmi_busy) {
increase_dmi_busy_delay(target);
}
if (execute_busy) {
increase_interrupt_high_delay(target);
}
if (dmi_busy || execute_busy) {
wait_for_debugint_clear(target, false);
// Retry.
LOG_INFO("Retrying memory read starting from 0x%x with more delays",
address + size * i);
} else {
i += batch_size;
for (uint32_t i = 0; i < count; i++) {
uint32_t value = dmi_read(target, DMI_DATA0);
switch (size) {
case 1:
buffer[i] = value;
break;
case 2:
buffer[2*i] = value;
buffer[2*i+1] = value >> 8;
break;
case 4:
buffer[4*i] = value;
buffer[4*i+1] = value >> 8;
buffer[4*i+2] = value >> 16;
buffer[4*i+3] = value >> 24;
break;
default:
return ERROR_FAIL;
}
}
if (result_value != 0) {
LOG_USER("Core got an exception (0x%x) while reading from 0x%x",
result_value, address + size * (count-1));
if (count > 1) {
LOG_USER("(It may have failed between 0x%x and 0x%x as well, but we "
"didn't check then.)",
address, address + size * (count-2) + size - 1);
}
goto error;
}
scans_delete(scans);
cache_clean(target);
return ERROR_OK;
error:
scans_delete(scans);
cache_clean(target);
return ERROR_FAIL;
}
static int setup_write_memory(struct target *target, uint32_t size)
{
switch (size) {
case 1:
cache_set32(target, 0, lb(S0, ZERO, DEBUG_RAM_START + 16));
cache_set32(target, 1, sb(S0, T0, 0));
break;
case 2:
cache_set32(target, 0, lh(S0, ZERO, DEBUG_RAM_START + 16));
cache_set32(target, 1, sh(S0, T0, 0));
break;
case 4:
cache_set32(target, 0, lw(S0, ZERO, DEBUG_RAM_START + 16));
cache_set32(target, 1, sw(S0, T0, 0));
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
}
cache_set32(target, 2, addi(T0, T0, size));
cache_set_jump(target, 3);
cache_write(target, 4, false);
dmi_write(target, DMI_ABSTRACTCS, 0);
// TODO: Check for errors.
return ERROR_OK;
}
@ -2483,143 +2360,61 @@ static int setup_write_memory(struct target *target, uint32_t size)
static int write_memory(struct target *target, uint32_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
riscv013_info_t *info = get_info(target);
select_dmi(target);
// Set up the address.
cache_set_store(target, 0, T0, SLOT1);
cache_set_load(target, 1, T0, SLOT0);
cache_set_jump(target, 2);
cache_set(target, SLOT0, address);
if (cache_write(target, 5, true) != ERROR_OK) {
return ERROR_FAIL;
abstract_write_register(target, S0, xlen(target), address);
program_t *program = program_new();
switch (size) {
case 1:
program_add32(program, sb(S1, S0, 0));
break;
case 2:
program_add32(program, sh(S1, S0, 0));
break;
case 4:
program_add32(program, sw(S1, S0, 0));
break;
default:
LOG_ERROR("Unsupported size: %d", size);
return ERROR_FAIL;
}
program_add32(program, addi(S0, S0, size));
program_add32(program, ebreak());
write_program(target, program);
program_delete(program);
uint64_t t0 = cache_get(target, SLOT1);
LOG_DEBUG("t0 is 0x%" PRIx64, t0);
if (setup_write_memory(target, size) != ERROR_OK) {
return ERROR_FAIL;
}
const int max_batch_size = 256;
scans_t *scans = scans_new(target, max_batch_size);
uint32_t result_value = 0x777;
uint32_t i = 0;
while (i < count + 2) {
unsigned int batch_size = MIN(count + 2 - i, max_batch_size);
scans_reset(scans);
for (unsigned int j = 0; j < batch_size; j++) {
if (i + j >= count) {
// Check for an exception.
scans_add_read32(scans, info->dramsize-1, false);
} else {
// Write the next value and set interrupt.
uint32_t value;
uint32_t offset = size * (i + j);
switch (size) {
case 1:
value = buffer[offset];
break;
case 2:
value = buffer[offset] |
(buffer[offset+1] << 8);
break;
case 4:
value = buffer[offset] |
((uint32_t) buffer[offset+1] << 8) |
((uint32_t) buffer[offset+2] << 16) |
((uint32_t) buffer[offset+3] << 24);
break;
default:
goto error;
}
scans_add_write32(scans, 4, value, true);
}
for (uint32_t i = 0; i < count; i++) {
uint32_t value;
switch (size) {
case 1:
value = buffer[i];
break;
case 2:
value = buffer[2*i] | ((uint32_t) buffer[2*i+1] << 8);
break;
case 4:
value = buffer[4*i] |
((uint32_t) buffer[4*i+1] << 8) |
((uint32_t) buffer[4*i+2] << 8) |
((uint32_t) buffer[4*i+3] << 8);
break;
default:
return ERROR_FAIL;
}
dmi_write(target, DMI_DATA0, value);
int retval = scans_execute(scans);
if (retval != ERROR_OK) {
LOG_ERROR("JTAG execute failed: %d", retval);
goto error;
}
int dmi_busy = 0;
int execute_busy = 0;
for (unsigned int j = 0; j < batch_size; j++) {
dmi_status_t status = scans_get_u32(scans, j, DMI_OP_START,
DMI_OP_SIZE);
switch (status) {
case DMI_STATUS_SUCCESS:
break;
case DMI_STATUS_FAILED:
LOG_ERROR("Debug RAM write failed. Hardware error?");
goto error;
case DMI_STATUS_BUSY:
dmi_busy++;
break;
default:
LOG_ERROR("Got invalid bus access status: %d", status);
return ERROR_FAIL;
}
int interrupt = scans_get_u32(scans, j, DMI_DATA_START + 33, 1);
if (interrupt) {
execute_busy++;
}
if (i + j == count + 1) {
result_value = scans_get_u32(scans, j, DMI_DATA_START, 32);
}
}
if (dmi_busy) {
increase_dmi_busy_delay(target);
}
if (execute_busy) {
increase_interrupt_high_delay(target);
}
if (dmi_busy || execute_busy) {
wait_for_debugint_clear(target, false);
// Retry.
// Set t0 back to what it should have been at the beginning of this
// batch.
LOG_INFO("Retrying memory write starting from 0x%x with more delays",
address + size * i);
cache_clean(target);
if (write_gpr(target, T0, address + size * i) != ERROR_OK) {
goto error;
}
if (setup_write_memory(target, size) != ERROR_OK) {
goto error;
}
} else {
i += batch_size;
if (i == 0) {
execute_abstract_command(target,
AC_ACCESS_REGISTER_WRITE | AC_ACCESS_REGISTER_POSTEXEC |
abstract_register_size(xlen(target)) | reg_number_to_no(S1));
dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_AUTOEXEC0);
}
}
dmi_write(target, DMI_ABSTRACTCS, 0);
// TODO: Check for errors.
if (result_value != 0) {
LOG_ERROR("Core got an exception (0x%x) while writing to 0x%x",
result_value, address + size * (count-1));
if (count > 1) {
LOG_ERROR("(It may have failed between 0x%x and 0x%x as well, but we "
"didn't check then.)",
address, address + size * (count-2) + size - 1);
}
goto error;
}
cache_clean(target);
return register_write(target, T0, t0);
error:
scans_delete(scans);
cache_clean(target);
return ERROR_FAIL;
return ERROR_OK;
}
static int arch_state(struct target *target)