aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/target/riscv/riscv-013.c376
-rw-r--r--src/target/riscv/riscv.c42
-rw-r--r--src/target/riscv/riscv.h4
3 files changed, 413 insertions, 9 deletions
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 4e3deab..34ec3c6 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -65,7 +65,13 @@ 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);
+static int riscv013_test_sba_config_reg(struct target *target, target_addr_t legal_address,
+ uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test);
+void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t* write_data,
+ uint32_t write_size, uint32_t sbcs);
+void read_memory_sba_simple(struct target *target, target_addr_t addr,
+ uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs);
+static int riscv013_test_compliance(struct target *target);
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@@ -1588,6 +1594,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_sba_config_reg = &riscv013_test_sba_config_reg;
generic_info->test_compliance = &riscv013_test_compliance;
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
@@ -2935,6 +2942,357 @@ void riscv013_fill_dmi_nop_u64(struct target *target, char *buf)
buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0);
}
+/* Helper function for riscv013_test_sba_config_reg */
+static int get_max_sbaccess(struct target *target)
+{
+ RISCV013_INFO(info);
+
+ uint32_t sbaccess128 = get_field(info->sbcs, DMI_SBCS_SBACCESS128);
+ uint32_t sbaccess64 = get_field(info->sbcs, DMI_SBCS_SBACCESS64);
+ uint32_t sbaccess32 = get_field(info->sbcs, DMI_SBCS_SBACCESS32);
+ uint32_t sbaccess16 = get_field(info->sbcs, DMI_SBCS_SBACCESS16);
+ uint32_t sbaccess8 = get_field(info->sbcs, DMI_SBCS_SBACCESS8);
+
+ if (sbaccess128)
+ return 4;
+ else if (sbaccess64)
+ return 3;
+ else if (sbaccess32)
+ return 2;
+ else if (sbaccess16)
+ return 1;
+ else if (sbaccess8)
+ return 0;
+ else
+ return -1;
+}
+
+static uint32_t get_num_sbdata_regs(struct target *target)
+{
+ RISCV013_INFO(info);
+
+ uint32_t sbaccess128 = get_field(info->sbcs, DMI_SBCS_SBACCESS128);
+ uint32_t sbaccess64 = get_field(info->sbcs, DMI_SBCS_SBACCESS64);
+ uint32_t sbaccess32 = get_field(info->sbcs, DMI_SBCS_SBACCESS32);
+
+ if (sbaccess128)
+ return 4;
+ else if (sbaccess64)
+ return 2;
+ else if (sbaccess32)
+ return 1;
+ else
+ return 0;
+}
+
+static int riscv013_test_sba_config_reg(struct target *target,
+ target_addr_t legal_address, uint32_t num_words,
+ target_addr_t illegal_address, bool run_sbbusyerror_test)
+{
+ LOG_INFO("Testing System Bus Access as defined by RISC-V Debug Spec v0.13");
+
+ uint32_t tests_failed = 0;
+
+ uint32_t rd_val;
+ uint32_t sbcs_orig;
+ dmi_read(target, &sbcs_orig, DMI_SBCS);
+
+ uint32_t sbcs = sbcs_orig;
+ bool test_passed;
+
+ int max_sbaccess = get_max_sbaccess(target);
+
+ if (max_sbaccess == -1) {
+ LOG_ERROR("System Bus Access not supported in this config.");
+ return ERROR_FAIL;
+ }
+
+ if (get_field(sbcs, DMI_SBCS_SBVERSION) != 1) {
+ LOG_ERROR("System Bus Access unsupported SBVERSION (%d). Only version 1 is supported.",
+ get_field(sbcs, DMI_SBCS_SBVERSION));
+ return ERROR_FAIL;
+ }
+
+ uint32_t num_sbdata_regs = get_num_sbdata_regs(target);
+
+ uint32_t rd_buf[num_sbdata_regs];
+
+ /* Test 1: Simple write/read test */
+ test_passed = true;
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBAUTOINCREMENT, 0);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ uint32_t test_patterns[4] = {0xdeadbeef, 0xfeedbabe, 0x12345678, 0x08675309};
+ for (uint32_t sbaccess = 0; sbaccess <= (uint32_t)max_sbaccess; sbaccess++) {
+ sbcs = set_field(sbcs, DMI_SBCS_SBACCESS, sbaccess);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ uint32_t compare_mask = (sbaccess == 0) ? 0xff : (sbaccess == 1) ? 0xffff : 0xffffffff;
+
+ for (uint32_t i = 0; i < num_words; i++) {
+ uint32_t addr = legal_address + (i << sbaccess);
+ uint32_t wr_data[num_sbdata_regs];
+ for (uint32_t j = 0; j < num_sbdata_regs; j++)
+ wr_data[j] = test_patterns[j] + i;
+ write_memory_sba_simple(target, addr, wr_data, num_sbdata_regs, sbcs);
+ }
+
+ for (uint32_t i = 0; i < num_words; i++) {
+ uint32_t addr = legal_address + (i << sbaccess);
+ read_memory_sba_simple(target, addr, rd_buf, num_sbdata_regs, sbcs);
+ for (uint32_t j = 0; j < num_sbdata_regs; j++) {
+ if (((test_patterns[j]+i)&compare_mask) != (rd_buf[j]&compare_mask)) {
+ LOG_ERROR("System Bus Access Test 1: Error reading non-autoincremented address %x,"
+ "expected val = %x, read val = %x", addr, test_patterns[j]+i, rd_buf[j]);
+ test_passed = false;
+ tests_failed++;
+ }
+ }
+ }
+ }
+ if (test_passed)
+ LOG_INFO("System Bus Access Test 1: Simple write/read test PASSED.");
+
+ /* Test 2: Address autoincrement test */
+ target_addr_t curr_addr;
+ target_addr_t prev_addr;
+ test_passed = true;
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBAUTOINCREMENT, 1);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ for (uint32_t sbaccess = 0; sbaccess <= (uint32_t)max_sbaccess; sbaccess++) {
+ sbcs = set_field(sbcs, DMI_SBCS_SBACCESS, sbaccess);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ dmi_write(target, DMI_SBADDRESS0, legal_address);
+ read_sbcs_nonbusy(target, &sbcs);
+ curr_addr = legal_address;
+ for (uint32_t i = 0; i < num_words; i++) {
+ prev_addr = curr_addr;
+ read_sbcs_nonbusy(target, &sbcs);
+ curr_addr = sb_read_address(target);
+ if ((curr_addr - prev_addr != (uint32_t)(1 << sbaccess)) && (i != 0)) {
+ LOG_ERROR("System Bus Access Test 2: Error with address auto-increment, sbaccess = %x.", sbaccess);
+ test_passed = false;
+ tests_failed++;
+ }
+ dmi_write(target, DMI_SBDATA0, i);
+ }
+
+ read_sbcs_nonbusy(target, &sbcs);
+
+ dmi_write(target, DMI_SBADDRESS0, legal_address);
+
+ uint32_t val;
+ sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 1);
+ dmi_write(target, DMI_SBCS, sbcs);
+ dmi_read(target, &val, DMI_SBDATA0); /* Dummy read to trigger first system bus read */
+ curr_addr = legal_address;
+ for (uint32_t i = 0; i < num_words; i++) {
+ prev_addr = curr_addr;
+ read_sbcs_nonbusy(target, &sbcs);
+ curr_addr = sb_read_address(target);
+ if ((curr_addr - prev_addr != (uint32_t)(1 << sbaccess)) && (i != 0)) {
+ LOG_ERROR("System Bus Access Test 2: Error with address auto-increment, sbaccess = %x", sbaccess);
+ test_passed = false;
+ tests_failed++;
+ }
+ dmi_read(target, &val, DMI_SBDATA0);
+ read_sbcs_nonbusy(target, &sbcs);
+ if (i != val) {
+ LOG_ERROR("System Bus Access Test 2: Error reading auto-incremented address,"
+ "expected val = %x, read val = %x.", i, val);
+ test_passed = false;
+ tests_failed++;
+ }
+ }
+ }
+ if (test_passed)
+ LOG_INFO("System Bus Access Test 2: Address auto-increment test PASSED.");
+
+ /* Test 3: Read from illegal address */
+ read_memory_sba_simple(target, illegal_address, rd_buf, 1, sbcs_orig);
+
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 2) {
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 2);
+ dmi_write(target, DMI_SBCS, sbcs);
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 0)
+ LOG_INFO("System Bus Access Test 3: Illegal address read test PASSED.");
+ else
+ LOG_ERROR("System Bus Access Test 3: Illegal address read test FAILED, unable to clear to 0.");
+ } else {
+ LOG_ERROR("System Bus Access Test 3: Illegal address read test FAILED, unable to set error code.");
+ }
+
+ /* Test 4: Write to illegal address */
+ write_memory_sba_simple(target, illegal_address, test_patterns, 1, sbcs_orig);
+
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 2) {
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 2);
+ dmi_write(target, DMI_SBCS, sbcs);
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 0)
+ LOG_INFO("System Bus Access Test 4: Illegal address write test PASSED.");
+ else {
+ LOG_ERROR("System Bus Access Test 4: Illegal address write test FAILED, unable to clear to 0.");
+ tests_failed++;
+ }
+ } else {
+ LOG_ERROR("System Bus Access Test 4: Illegal address write test FAILED, unable to set error code.");
+ tests_failed++;
+ }
+
+ /* Test 5: Write with unsupported sbaccess size */
+ uint32_t sbaccess128 = get_field(sbcs_orig, DMI_SBCS_SBACCESS128);
+
+ if (sbaccess128) {
+ LOG_INFO("System Bus Access Test 5: SBCS sbaccess error test PASSED, all sbaccess sizes supported.");
+ } else {
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBACCESS, 4);
+
+ write_memory_sba_simple(target, legal_address, test_patterns, 1, sbcs);
+
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 4) {
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 4);
+ dmi_write(target, DMI_SBCS, sbcs);
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 0)
+ LOG_INFO("System Bus Access Test 5: SBCS sbaccess error test PASSED.");
+ else {
+ LOG_ERROR("System Bus Access Test 5: SBCS sbaccess error test FAILED, unable to clear to 0.");
+ tests_failed++;
+ }
+ } else {
+ LOG_ERROR("System Bus Access Test 5: SBCS sbaccess error test FAILED, unable to set error code.");
+ tests_failed++;
+ }
+ }
+
+ /* Test 6: Write to misaligned address */
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBACCESS, 1);
+
+ write_memory_sba_simple(target, legal_address+1, test_patterns, 1, sbcs);
+
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 3) {
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBERROR, 3);
+ dmi_write(target, DMI_SBCS, sbcs);
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBERROR) == 0)
+ LOG_INFO("System Bus Access Test 6: SBCS address alignment error test PASSED");
+ else {
+ LOG_ERROR("System Bus Access Test 6: SBCS address alignment error test FAILED, unable to clear to 0.");
+ tests_failed++;
+ }
+ } else {
+ LOG_ERROR("System Bus Access Test 6: SBCS address alignment error test FAILED, unable to set error code.");
+ tests_failed++;
+ }
+
+ /* Test 7: Set sbbusyerror, only run this case in simulation as it is likely
+ * impossible to hit otherwise */
+ if (run_sbbusyerror_test) {
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBREADONADDR, 1);
+ dmi_write(target, DMI_SBCS, sbcs);
+
+ for (int i = 0; i < 16; i++)
+ dmi_write(target, DMI_SBDATA0, 0xdeadbeef);
+
+ for (int i = 0; i < 16; i++)
+ dmi_write(target, DMI_SBADDRESS0, legal_address);
+
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBBUSYERROR)) {
+ sbcs = set_field(sbcs_orig, DMI_SBCS_SBBUSYERROR, 1);
+ dmi_write(target, DMI_SBCS, sbcs);
+ dmi_read(target, &rd_val, DMI_SBCS);
+ if (get_field(rd_val, DMI_SBCS_SBBUSYERROR) == 0)
+ LOG_INFO("System Bus Access Test 7: SBCS sbbusyerror test PASSED.");
+ else {
+ LOG_ERROR("System Bus Access Test 7: SBCS sbbusyerror test FAILED, unable to clear to 0.");
+ tests_failed++;
+ }
+ } else {
+ LOG_ERROR("System Bus Access Test 7: SBCS sbbusyerror test FAILED, unable to set error code.");
+ tests_failed++;
+ }
+ }
+
+ if (tests_failed == 0) {
+ LOG_INFO("ALL TESTS PASSED");
+ return ERROR_OK;
+ } else {
+ LOG_ERROR("%d TESTS FAILED", tests_failed);
+ return ERROR_FAIL;
+ }
+
+}
+
+void write_memory_sba_simple(struct target *target, target_addr_t addr,
+ uint32_t *write_data, uint32_t write_size, uint32_t sbcs)
+{
+ RISCV013_INFO(info);
+
+ uint32_t rd_sbcs;
+ uint32_t masked_addr;
+
+ uint32_t sba_size = get_field(info->sbcs, DMI_SBCS_SBASIZE);
+
+ read_sbcs_nonbusy(target, &rd_sbcs);
+
+ uint32_t sbcs_no_readonaddr = set_field(sbcs, DMI_SBCS_SBREADONADDR, 0);
+ dmi_write(target, DMI_SBCS, sbcs_no_readonaddr);
+
+ for (uint32_t i = 0; i < sba_size/32; i++) {
+ masked_addr = (addr >> 32*i) & 0xffffffff;
+
+ if (i != 3)
+ dmi_write(target, DMI_SBADDRESS0+i, masked_addr);
+ else
+ dmi_write(target, DMI_SBADDRESS3, masked_addr);
+ }
+
+ /* Write SBDATA registers starting with highest address, since write to
+ * SBDATA0 triggers write */
+ for (int i = write_size-1; i >= 0; i--)
+ dmi_write(target, DMI_SBDATA0+i, write_data[i]);
+}
+
+void read_memory_sba_simple(struct target *target, target_addr_t addr,
+ uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs)
+{
+ RISCV013_INFO(info);
+
+ uint32_t rd_sbcs;
+ uint32_t masked_addr;
+
+ uint32_t sba_size = get_field(info->sbcs, DMI_SBCS_SBASIZE);
+
+ read_sbcs_nonbusy(target, &rd_sbcs);
+
+ uint32_t sbcs_readonaddr = set_field(sbcs, DMI_SBCS_SBREADONADDR, 1);
+ dmi_write(target, DMI_SBCS, sbcs_readonaddr);
+
+ /* Write addresses starting with highest address register */
+ for (int i = sba_size/32-1; i >= 0; i--) {
+ masked_addr = (addr >> 32*i) & 0xffffffff;
+
+ if (i != 3)
+ dmi_write(target, DMI_SBADDRESS0+i, masked_addr);
+ else
+ dmi_write(target, DMI_SBADDRESS3, masked_addr);
+ }
+
+ read_sbcs_nonbusy(target, &rd_sbcs);
+
+ for (uint32_t i = 0; i < read_size; i++)
+ dmi_read(target, &(rd_buf[i]), DMI_SBDATA0+i);
+}
+
int riscv013_dmi_write_u64_bits(struct target *target)
{
RISCV013_INFO(info);
@@ -3143,8 +3501,8 @@ int riscv013_test_compliance(struct target *target)
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) {
+ 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;
@@ -3278,7 +3636,7 @@ int riscv013_test_compliance(struct target *target)
/* 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 = 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.");
@@ -3492,10 +3850,12 @@ int riscv013_test_compliance(struct target *target)
/* 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)
+ uint32_t failed_tests = total_tests - passed_tests;
+ if (total_tests == passed_tests) {
+ LOG_INFO("ALL TESTS PASSED\n");
return ERROR_OK;
- else
+ } else {
+ LOG_INFO("%d TESTS FAILED\n", failed_tests);
return ERROR_FAIL;
+ }
}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index de47d25..dceed61 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -1557,6 +1557,35 @@ COMMAND_HANDLER(riscv_dmi_write)
}
}
+COMMAND_HANDLER(riscv_test_sba_config_reg)
+{
+ if (CMD_ARGC != 4) {
+ LOG_ERROR("Command takes exactly 4 arguments");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ target_addr_t legal_address;
+ uint32_t num_words;
+ target_addr_t illegal_address;
+ bool run_sbbusyerror_test;
+
+ COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[0], legal_address);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], num_words);
+ COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[2], illegal_address);
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[3], run_sbbusyerror_test);
+
+ if (r->test_sba_config_reg) {
+ return r->test_sba_config_reg(target, legal_address, num_words,
+ illegal_address, run_sbbusyerror_test);
+ } else {
+ LOG_ERROR("test_sba_config_reg is not implemented for this target.");
+ return ERROR_FAIL;
+ }
+}
+
static const struct command_registration riscv_exec_command_handlers[] = {
{
.name = "test_compliance",
@@ -1633,6 +1662,19 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.usage = "riscv dmi_write address value",
.help = "Perform a 32-bit DMI write of value at address."
},
+ {
+ .name = "test_sba_config_reg",
+ .handler = riscv_test_sba_config_reg,
+ .mode = COMMAND_ANY,
+ .usage = "riscv test_sba_config_reg legal_address num_words"
+ "illegal_address run_sbbusyerror_test[on/off]",
+ .help = "Perform a series of tests on the SBCS register."
+ "Inputs are a legal, 128-byte aligned address and a number of words to"
+ "read/write starting at that address (i.e., address range [legal address,"
+ "legal_address+word_size*num_words) must be legally readable/writable)"
+ ", an illegal, 128-byte aligned address for error flag/handling cases,"
+ "and whether sbbusyerror test should be run."
+ },
COMMAND_REGISTRATION_DONE
};
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index 419d051..deca21e 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -126,8 +126,10 @@ 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);
+ int (*test_sba_config_reg)(struct target *target, target_addr_t legal_address,
+ uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test);
+ int (*test_compliance)(struct target *target);
} riscv_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/