Merge pull request #235 from rmac-sifive/sba_tests

Tests for SBA feature
mpsse_flush
Ryan Macdonald 2018-09-04 10:00:48 -07:00 committed by GitHub
commit 33aa058819
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 413 additions and 9 deletions

View File

@ -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;
}
}

View File

@ -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
};

View File

@ -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.*/