Merge pull request #137 from riscv/riscv-compliance

Add a RISC-V Compliance Test Command
This commit is contained in:
Megan Wachs 2018-08-30 17:09:08 -07:00 committed by GitHub
commit ebed910c2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 490 additions and 2 deletions

View File

@ -224,6 +224,9 @@ static uint32_t ebreak_c(void)
return MATCH_C_EBREAK;
}
static uint32_t wfi(void) __attribute__ ((unused));
static uint32_t wfi(void) { return MATCH_WFI; }
static uint32_t fence_i(void) __attribute__ ((unused));
static uint32_t fence_i(void)
{

View File

@ -52,8 +52,8 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
* memory. */
int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
/* Helpers to assembly various instructions. Return 0 on success. These might
* assembly into a multi-instruction sequence that overwrites some other
/* Helpers to assemble various instructions. Return 0 on success. These might
* assemble into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);

View File

@ -65,6 +65,7 @@ static int read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer);
static int write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer);
static int riscv013_test_compliance(struct target *target);
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@ -1587,6 +1588,7 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->authdata_write = &riscv013_authdata_write;
generic_info->dmi_read = &dmi_read;
generic_info->dmi_write = &dmi_write;
generic_info->test_compliance = &riscv013_test_compliance;
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
@ -3043,3 +3045,457 @@ void riscv013_clear_abstract_error(struct target *target)
/* Clear the error status. */
dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR);
}
#define COMPLIANCE_TEST(b, message) \
{ \
int pass = 0; \
if (b) { \
pass = 1; \
passed_tests++; \
} \
LOG_INFO("%s test %d (%s)\n", (pass) ? "PASSED" : "FAILED", total_tests, message); \
assert(pass); \
total_tests++; \
}
#define COMPLIANCE_MUST_PASS(b) COMPLIANCE_TEST(ERROR_OK == (b), "Regular calls must return ERROR_OK")
#define COMPLIANCE_READ(target, addr, value) COMPLIANCE_MUST_PASS(dmi_read(target, addr, value))
#define COMPLIANCE_WRITE(target, addr, value) COMPLIANCE_MUST_PASS(dmi_write(target, addr, value))
#define COMPLIANCE_CHECK_RO(target, addr) \
{ \
uint32_t orig; \
uint32_t inverse; \
COMPLIANCE_READ(target, &orig, addr); \
COMPLIANCE_WRITE(target, addr, ~orig); \
COMPLIANCE_READ(target, &inverse, addr); \
COMPLIANCE_TEST(orig == inverse, "Register must be read-only"); \
}
int riscv013_test_compliance(struct target *target)
{
LOG_INFO("Testing Compliance against RISC-V Debug Spec v0.13");
if (!riscv_rtos_enabled(target)) {
LOG_ERROR("Please run with -rtos riscv to run compliance test.");
return ERROR_FAIL;
}
int total_tests = 0;
int passed_tests = 0;
uint32_t dmcontrol_orig = DMI_DMCONTROL_DMACTIVE;
uint32_t dmcontrol;
uint32_t testvar;
uint32_t testvar_read;
riscv_reg_t value;
RISCV013_INFO(info);
/* All the bits of HARTSEL are covered by the examine sequence. */
/* hartreset */
/* This field is optional. Either we can read and write it to 1/0,
or it is tied to 0. This check doesn't really do anything, but
it does attempt to set the bit to 1 and then back to 0, which needs to
work if its implemented. */
COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 1));
COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 0));
COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL);
COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HARTRESET) == 0),
"DMCONTROL.hartreset can be 0 or RW.");
/* hasel */
COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 1));
COMPLIANCE_WRITE(target, DMI_DMCONTROL, set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 0));
COMPLIANCE_READ(target, &dmcontrol, DMI_DMCONTROL);
COMPLIANCE_TEST((get_field(dmcontrol, DMI_DMCONTROL_HASEL) == 0),
"DMCONTROL.hasel can be 0 or RW.");
/* TODO: test that hamask registers exist if hasel does. */
/* haltreq */
COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target));
/* This bit is not actually readable according to the spec, so nothing to check.*/
/* DMSTATUS */
COMPLIANCE_CHECK_RO(target, DMI_DMSTATUS);
/* resumereq */
/* This bit is not actually readable according to the spec, so nothing to check.*/
COMPLIANCE_MUST_PASS(riscv_resume_all_harts(target));
/* Halt all harts again so the test can continue.*/
COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target));
/* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */
uint32_t hartinfo;
COMPLIANCE_READ(target, &hartinfo, DMI_HARTINFO);
for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
COMPLIANCE_CHECK_RO(target, DMI_HARTINFO);
/* $dscratch CSRs */
uint32_t nscratch = get_field(hartinfo, DMI_HARTINFO_NSCRATCH);
for (unsigned int d = 0; d < nscratch; d++) {
riscv_reg_t testval, testval_read;
/* Because DSCRATCH is not guaranteed to last across PB executions, need to put
this all into one PB execution. Which may not be possible on all implementations.*/
if (info->progbufsize >= 5) {
for (testval = 0x0011223300112233;
testval != 0xDEAD;
testval = testval == 0x0011223300112233 ? ~testval : 0xDEAD) {
COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, testval) == ERROR_OK,
"Need to be able to write S0 in order to test DSCRATCH.");
struct riscv_program program32;
riscv_program_init(&program32, target);
riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH + d);
riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH + d);
riscv_program_fence(&program32);
riscv_program_ebreak(&program32);
COMPLIANCE_TEST(riscv_program_exec(&program32, target) == ERROR_OK,
"Accessing DSCRATCH with program buffer should succeed.");
COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_S1) == ERROR_OK,
"Need to be able to read S1 in order to test DSCRATCH.");
if (riscv_xlen(target) > 32) {
COMPLIANCE_TEST(testval == testval_read,
"All DSCRATCH registers in HARTINFO must be R/W.");
} else {
COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF),
"All DSCRATCH registers in HARTINFO must be R/W.");
}
}
}
}
/* TODO: dataaccess */
if (get_field(hartinfo, DMI_HARTINFO_DATAACCESS)) {
/* TODO: Shadowed in memory map. */
/* TODO: datasize */
/* TODO: dataaddr */
} else {
/* TODO: Shadowed in CSRs. */
/* TODO: datasize */
/* TODO: dataaddr */
}
}
/* HALTSUM -- TODO: More than 32 harts. Would need to loop over this to set hartsel */
/* TODO: HALTSUM2, HALTSUM3 */
/* HALTSUM0 */
uint32_t expected_haltsum0 = 0;
for (int i = 0; i < MIN(riscv_count_harts(target), 32); i++)
expected_haltsum0 |= (1 << i);
COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0);
COMPLIANCE_TEST(testvar_read == expected_haltsum0,
"HALTSUM0 should report summary of up to 32 halted harts");
COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0xffffffff);
COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0);
COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
COMPLIANCE_WRITE(target, DMI_HALTSUM0, 0x0);
COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM0);
COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
/* HALTSUM1 */
uint32_t expected_haltsum1 = 0;
for (int i = 0; i < MIN(riscv_count_harts(target), 1024); i += 32)
expected_haltsum1 |= (1 << (i/32));
COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1);
COMPLIANCE_TEST(testvar_read == expected_haltsum1,
"HALTSUM1 should report summary of up to 1024 halted harts");
COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0xffffffff);
COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1);
COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
COMPLIANCE_WRITE(target, DMI_HALTSUM1, 0x0);
COMPLIANCE_READ(target, &testvar_read, DMI_HALTSUM1);
COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
/* TODO: HAWINDOWSEL */
/* TODO: HAWINDOW */
/* ABSTRACTCS */
uint32_t abstractcs;
COMPLIANCE_READ(target, &abstractcs, DMI_ABSTRACTCS);
/* Check that all reported Data Words are really R/W */
for (int invert = 0; invert < 2; invert++) {
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
testvar = (i + 1) * 0x11111111;
if (invert)
testvar = ~testvar;
COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar);
}
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
testvar = (i + 1) * 0x11111111;
if (invert)
testvar = ~testvar;
COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
COMPLIANCE_TEST(testvar_read == testvar, "All reported DATA words must be R/W");
}
}
/* Check that all reported ProgBuf words are really R/W */
for (int invert = 0; invert < 2; invert++) {
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
testvar = (i + 1) * 0x11111111;
if (invert)
testvar = ~testvar;
COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar);
}
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
testvar = (i + 1) * 0x11111111;
if (invert)
testvar = ~testvar;
COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
COMPLIANCE_TEST(testvar_read == testvar, "All reported PROGBUF words must be R/W");
}
}
/* TODO: Cause and clear all error types */
/* COMMAND
According to the spec, this register is only W, so can't really check the read result.
But at any rate, this is not legal and should cause an error. */
COMPLIANCE_WRITE(target, DMI_COMMAND, 0xAAAAAAAA);
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \
"Illegal COMMAND should result in UNSUPPORTED");
COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
COMPLIANCE_WRITE(target, DMI_COMMAND, 0x55555555);
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \
"Illegal COMMAND should result in UNSUPPORTED");
COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
/* Basic Abstract Commands */
for (unsigned int i = 1; i < 32; i = i << 1) {
riscv_reg_t testval = i | ((i + 1ULL) << 32);
riscv_reg_t testval_read;
COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_ZERO + i, testval),
"GPR Writes should be supported.");
COMPLIANCE_MUST_PASS(write_abstract_arg(target, 0, 0xDEADBEEFDEADBEEF, 64));
COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i),
"GPR Reads should be supported.");
if (riscv_xlen(target) > 32) {
/* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */
COMPLIANCE_TEST(testval == testval_read, "GPR Reads and writes should be supported.");
} else {
/* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */
COMPLIANCE_TEST((testval & 0xFFFFFFFF) == testval_read, "GPR Reads and writes should be supported.");
}
}
/* ABSTRACTAUTO
See which bits are actually writable */
COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
uint32_t abstractauto;
uint32_t busy;
COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO);
COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0);
if (abstractauto > 0) {
/* This mechanism only works when you have a reasonable sized progbuf, which is not
a true compliance requirement. */
if (info->progbufsize >= 3) {
testvar = 0;
COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_S0, 0),
"Need to be able to write S0 to test ABSTRACTAUTO");
struct riscv_program program;
COMPLIANCE_MUST_PASS(riscv_program_init(&program, target));
/* This is also testing that WFI() is a NOP during debug mode. */
COMPLIANCE_MUST_PASS(riscv_program_insert(&program, wfi()));
COMPLIANCE_MUST_PASS(riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, 1));
COMPLIANCE_MUST_PASS(riscv_program_ebreak(&program));
COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0x0);
COMPLIANCE_MUST_PASS(riscv_program_exec(&program, target));
testvar++;
COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO);
uint32_t autoexec_data = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECDATA);
uint32_t autoexec_progbuf = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
for (unsigned int i = 0; i < 12; i++) {
COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
do {
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY);
} while (busy);
if (autoexec_data & (1 << i)) {
COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT),
"AUTOEXEC may be writable up to DATACOUNT bits.");
testvar++;
}
}
for (unsigned int i = 0; i < 16; i++) {
COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
do {
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY);
} while (busy);
if (autoexec_progbuf & (1 << i)) {
COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE),
"AUTOEXEC may be writable up to PROGBUFSIZE bits.");
testvar++;
}
}
COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0);
COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &value, GDB_REGNO_S0),
"Need to be able to read S0 to test ABSTRACTAUTO");
COMPLIANCE_TEST(testvar == value,
"ABSTRACTAUTO should cause COMMAND to run the expected number of times.");
}
}
/* Single-Step each hart. */
for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
COMPLIANCE_MUST_PASS(riscv013_on_step(target));
COMPLIANCE_MUST_PASS(riscv013_step_current_hart(target));
COMPLIANCE_TEST(riscv_halt_reason(target, hartsel) == RISCV_HALT_SINGLESTEP,
"Single Step should result in SINGLESTEP");
}
/* Core Register Tests */
uint64_t bogus_dpc = 0xdeadbeef;
for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
/* DCSR Tests */
COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0x0));
COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR));
COMPLIANCE_TEST(value != 0, "Not all bits in DCSR are writable by Debugger");
COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0xFFFFFFFF));
COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR));
COMPLIANCE_TEST(value != 0, "At least some bits in DCSR must be 1");
/* DPC. Note that DPC is sign-extended. */
riscv_reg_t dpcmask = 0xFFFFFFFCUL;
riscv_reg_t dpc;
if (riscv_xlen(target) > 32)
dpcmask |= (0xFFFFFFFFULL << 32);
if (riscv_supports_extension(target, riscv_current_hartid(target), 'C'))
dpcmask |= 0x2;
COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, dpcmask));
COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC));
COMPLIANCE_TEST(dpcmask == dpc,
"DPC must be sign-extended to XLEN and writable to all-1s (except the least significant bits)");
COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, 0));
COMPLIANCE_MUST_PASS(register_read_direct(target, &dpc, GDB_REGNO_DPC));
COMPLIANCE_TEST(dpc == 0, "DPC must be writable to 0.");
if (hartsel == 0)
bogus_dpc = dpc; /* For a later test step */
}
/* NDMRESET
Asserting non-debug module reset should not reset Debug Module state.
But it should reset Hart State, e.g. DPC should get a different value.
Also make sure that DCSR reports cause of 'HALT' even though previously we single-stepped.
*/
/* Write some registers. They should not be impacted by ndmreset. */
COMPLIANCE_WRITE(target, DMI_COMMAND, 0xFFFFFFFF);
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
testvar = (i + 1) * 0x11111111;
COMPLIANCE_WRITE(target, DMI_PROGBUF0 + i, testvar);
}
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
testvar = (i + 1) * 0x11111111;
COMPLIANCE_WRITE(target, DMI_DATA0 + i, testvar);
}
COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
COMPLIANCE_READ(target, &abstractauto, DMI_ABSTRACTAUTO);
/* Pulse reset. */
target->reset_halt = true;
COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, 0));
COMPLIANCE_TEST(ERROR_OK == assert_reset(target), "Must be able to assert NDMRESET");
COMPLIANCE_TEST(ERROR_OK == deassert_reset(target), "Must be able to deassert NDMRESET");
/* Verify that most stuff is not affected by ndmreset. */
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED,
"NDMRESET should not affect DMI_ABSTRACTCS");
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO);
COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DMI_ABSTRACTAUTO");
/* Clean up to avoid future test failures */
COMPLIANCE_WRITE(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
COMPLIANCE_WRITE(target, DMI_ABSTRACTAUTO, 0);
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
testvar = (i + 1) * 0x11111111;
COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
COMPLIANCE_TEST(testvar_read == testvar, "PROGBUF words must not be affected by NDMRESET");
}
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
testvar = (i + 1) * 0x11111111;
COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
COMPLIANCE_TEST(testvar_read == testvar, "DATA words must not be affected by NDMRESET");
}
/* Verify that DPC *is* affected by ndmreset. Since we don't know what it *should* be,
just verify that at least it's not the bogus value anymore. */
COMPLIANCE_TEST(bogus_dpc != 0xdeadbeef, "BOGUS DPC should have been set somehow (bug in compliance test)");
COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DPC));
COMPLIANCE_TEST(bogus_dpc != value, "NDMRESET should move DPC to reset value.");
COMPLIANCE_TEST(riscv_halt_reason(target, 0) == RISCV_HALT_INTERRUPT,
"After NDMRESET halt, DCSR should report cause of halt");
/* DMACTIVE -- deasserting DMACTIVE should reset all the above values. */
/* Toggle dmactive */
COMPLIANCE_WRITE(target, DMI_DMCONTROL, 0);
COMPLIANCE_WRITE(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTCS);
COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == 0, "ABSTRACTCS.cmderr should reset to 0");
COMPLIANCE_READ(target, &testvar_read, DMI_ABSTRACTAUTO);
COMPLIANCE_TEST(testvar_read == 0, "ABSTRACTAUTO should reset to 0");
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
COMPLIANCE_READ(target, &testvar_read, DMI_PROGBUF0 + i);
COMPLIANCE_TEST(testvar_read == 0, "PROGBUF words should reset to 0");
}
for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
COMPLIANCE_READ(target, &testvar_read, DMI_DATA0 + i);
COMPLIANCE_TEST(testvar_read == 0, "DATA words should reset to 0");
}
/*
* TODO:
* DCSR.cause priorities
* DCSR.stoptime/stopcycle
* DCSR.stepie
* DCSR.ebreak
* DCSR.prv
*/
/* Halt every hart for any follow-up tests*/
COMPLIANCE_MUST_PASS(riscv_halt_all_harts(target));
LOG_INFO("PASSED %d of %d TESTS\n", passed_tests, total_tests);
if (total_tests == passed_tests)
return ERROR_OK;
else
return ERROR_FAIL;
}

View File

@ -1323,6 +1323,25 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec)
return ERROR_OK;
}
COMMAND_HANDLER(riscv_test_compliance) {
struct target *target = get_current_target(CMD_CTX);
RISCV_INFO(r);
if (CMD_ARGC > 0) {
LOG_ERROR("Command does not take any parameters.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (r->test_compliance) {
return r->test_compliance(target);
} else {
LOG_ERROR("This target does not support this command (may implement an older version of the spec).");
return ERROR_FAIL;
}
}
COMMAND_HANDLER(riscv_set_prefer_sba)
{
if (CMD_ARGC != 1) {
@ -1539,6 +1558,13 @@ COMMAND_HANDLER(riscv_dmi_write)
}
static const struct command_registration riscv_exec_command_handlers[] = {
{
.name = "test_compliance",
.handler = riscv_test_compliance,
.mode = COMMAND_EXEC,
.usage = "riscv test_compliance",
.help = "Runs a basic compliance test suite against the RISC-V Debug Spec."
},
{
.name = "set_command_timeout_sec",
.handler = riscv_set_command_timeout_sec,

View File

@ -125,6 +125,9 @@ typedef struct {
int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address);
int (*dmi_write)(struct target *target, uint32_t address, uint32_t value);
int (*test_compliance)(struct target *target);
} riscv_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/