Add support for v1 system bus access.

This is functional, but doesn't handle errors.

Change-Id: Ifb46af1b0b567f3c2a6135b2ad5eb7ba63a3f595
This commit is contained in:
Tim Newsome 2018-01-18 14:32:01 -08:00
parent beac00149c
commit b67379700b
2 changed files with 240 additions and 19 deletions

View File

@ -1041,17 +1041,47 @@
#define DMI_AUTHDATA_DATA_OFFSET 0
#define DMI_AUTHDATA_DATA_LENGTH 32
#define DMI_AUTHDATA_DATA (0xffffffffU << DMI_AUTHDATA_DATA_OFFSET)
#define DMI_SBADDRESS3 0x37
/*
* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
* the system address bus is that wide).
*/
#define DMI_SBADDRESS3_ADDRESS_OFFSET 0
#define DMI_SBADDRESS3_ADDRESS_LENGTH 32
#define DMI_SBADDRESS3_ADDRESS (0xffffffffU << DMI_SBADDRESS3_ADDRESS_OFFSET)
#define DMI_SBCS 0x38
/*
* When a 1, every write to \Rsbaddresszero automatically triggers a
* 0: The System Bus interface conforms to mainline drafts of this
* spec older than 1 January, 2018.
*
* 1: The System Bus interface conforms to this version of the spec.
*
* Other values are reserved for future versions.
*/
#define DMI_SBCS_SBVERSION_OFFSET 29
#define DMI_SBCS_SBVERSION_LENGTH 3
#define DMI_SBCS_SBVERSION (0x7U << DMI_SBCS_SBVERSION_OFFSET)
/*
* When 1, indicates the system bus master is busy. (Whether the
* system bus itself is busy is related, but not the same thing.) This
* bit goes high immediately when a read or write is requested for any
* reason, and does not go low until the access is fully completed.
*
* To avoid race conditions, debuggers must not try to clear \Fsberror
* until they read \Fsbbusy as 0.
*/
#define DMI_SBCS_SBBUSY_OFFSET 21
#define DMI_SBCS_SBBUSY_LENGTH 1
#define DMI_SBCS_SBBUSY (0x1U << DMI_SBCS_SBBUSY_OFFSET)
/*
* When 1, every write to \Rsbaddresszero automatically triggers a
* system bus read at the new address.
*/
#define DMI_SBCS_SBREADONADDR_OFFSET 20
#define DMI_SBCS_SBREADONADDR_LENGTH 1
#define DMI_SBCS_SBREADONADDR (0x1U << DMI_SBCS_SBREADONADDR_OFFSET)
/*
* Select the access size to use for system bus accesses triggered by
* writes to \Rsbaddresszero or \Rsbdatazero.
* Select the access size to use for system bus accesses.
*
* 0: 8-bit
*
@ -1063,8 +1093,8 @@
*
* 4: 128-bit
*
* If an unsupported system bus access size is written here, the DM
* does not perform the access and sberror is set to 3.
* If \Fsbaccess has an unsupported value when the DM starts a bus
* access, the access is not performed and \Fsberror is set to 3.
*/
#define DMI_SBCS_SBACCESS_OFFSET 17
#define DMI_SBCS_SBACCESS_LENGTH 3
@ -1098,9 +1128,8 @@
*
* 3: There was some other error (eg. alignment).
*
* 4: The system bus master was busy when one of the
* {\tt sbaddress} or {\tt sbdata} registers was written,
* or \Rsbdatazero was read when it had stale data.
* 4: The system bus master was busy when one of the {\tt sbaddress}
* was written, or one of the {\tt sbdata} registers was accessed.
*/
#define DMI_SBCS_SBERROR_OFFSET 12
#define DMI_SBCS_SBERROR_LENGTH 3

View File

@ -143,6 +143,9 @@ typedef struct {
/* Number of words in the Program Buffer. */
unsigned progbufsize;
/* We cache the read-only bits of sbcs here. */
uint32_t sbcs;
yes_no_maybe_t progbuf_writable;
/* We only need the address so that we know the alignment of the buffer. */
riscv_addr_t progbuf_address;
@ -218,6 +221,18 @@ static void decode_dmi(char *text, unsigned address, unsigned data)
{ DMI_ABSTRACTCS, DMI_ABSTRACTCS_DATACOUNT, "datacount" },
{ DMI_COMMAND, DMI_COMMAND_CMDTYPE, "cmdtype" },
{ DMI_SBCS, DMI_SBCS_SBREADONADDR, "sbreadonaddr" },
{ DMI_SBCS, DMI_SBCS_SBACCESS, "sbaccess" },
{ DMI_SBCS, DMI_SBCS_SBAUTOINCREMENT, "sbautoincrement" },
{ DMI_SBCS, DMI_SBCS_SBREADONDATA, "sbreadondata" },
{ DMI_SBCS, DMI_SBCS_SBERROR, "sberror" },
{ DMI_SBCS, DMI_SBCS_SBASIZE, "sbasize" },
{ DMI_SBCS, DMI_SBCS_SBACCESS128, "sbaccess128" },
{ DMI_SBCS, DMI_SBCS_SBACCESS64, "sbaccess64" },
{ DMI_SBCS, DMI_SBCS_SBACCESS32, "sbaccess32" },
{ DMI_SBCS, DMI_SBCS_SBACCESS16, "sbaccess16" },
{ DMI_SBCS, DMI_SBCS_SBACCESS8, "sbaccess8" },
};
text[0] = 0;
@ -1148,6 +1163,7 @@ static int examine(struct target *target)
info->dtmcontrol_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
uint32_t dmstatus = dmi_read(target, DMI_DMSTATUS);
LOG_DEBUG("dmstatus: 0x%08x", dmstatus);
if (get_field(dmstatus, DMI_DMSTATUS_VERSION) != 2) {
LOG_ERROR("OpenOCD only supports Debug Module version 2, not %d "
"(dmstatus=0x%x)", get_field(dmstatus, DMI_DMSTATUS_VERSION), dmstatus);
@ -1158,16 +1174,7 @@ static int examine(struct target *target)
dmi_write(target, DMI_DMCONTROL, 0);
dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
uint32_t dmcontrol = dmi_read(target, DMI_DMCONTROL);
uint32_t hartinfo = dmi_read(target, DMI_HARTINFO);
LOG_DEBUG("dmcontrol: 0x%08x", dmcontrol);
LOG_DEBUG("dmstatus: 0x%08x", dmstatus);
LOG_DEBUG("hartinfo: 0x%08x", hartinfo);
info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE);
info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS);
info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR);
if (!get_field(dmcontrol, DMI_DMCONTROL_DMACTIVE)) {
LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
@ -1175,6 +1182,13 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
uint32_t hartinfo = dmi_read(target, DMI_HARTINFO);
LOG_DEBUG("hartinfo: 0x%08x", hartinfo);
info->datasize = get_field(hartinfo, DMI_HARTINFO_DATASIZE);
info->dataaccess = get_field(hartinfo, DMI_HARTINFO_DATAACCESS);
info->dataaddr = get_field(hartinfo, DMI_HARTINFO_DATAADDR);
if (!get_field(dmstatus, DMI_DMSTATUS_AUTHENTICATED)) {
LOG_ERROR("Authentication required by RISC-V core but not "
"supported by OpenOCD. dmcontrol=0x%x", dmcontrol);
@ -1191,6 +1205,8 @@ static int examine(struct target *target)
return ERROR_FAIL;
}
info->sbcs = dmi_read(target, DMI_SBCS);
/* Check that abstract data registers are accessible. */
uint32_t abstractcs = dmi_read(target, DMI_ABSTRACTCS);
info->datacount = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
@ -1439,11 +1455,102 @@ static void log_memory_access(target_addr_t address, uint64_t value,
LOG_DEBUG(fmt, value);
}
/* Read the relevant sbdata regs depending on size, and put the results into
* buffer. */
static int read_memory_bus_word(struct target *target, uint32_t size,
uint8_t *buffer)
{
if (size > 12)
write_to_buf(buffer + 12, dmi_read(target, DMI_SBDATA3), 4);
if (size > 8)
write_to_buf(buffer + 8, dmi_read(target, DMI_SBDATA2), 4);
if (size > 4)
write_to_buf(buffer + 4, dmi_read(target, DMI_SBDATA1), 4);
write_to_buf(buffer, dmi_read(target, DMI_SBDATA0), MIN(size, 4));
return ERROR_OK;
}
static uint32_t sb_sbaccess(unsigned size_bytes)
{
switch (size_bytes) {
case 1:
return set_field(0, DMI_SBCS_SBACCESS, 0);
case 2:
return set_field(0, DMI_SBCS_SBACCESS, 1);
case 4:
return set_field(0, DMI_SBCS_SBACCESS, 2);
case 8:
return set_field(0, DMI_SBCS_SBACCESS, 3);
case 16:
return set_field(0, DMI_SBCS_SBACCESS, 4);
}
assert(0);
}
static int sb_write_address(struct target *target, target_addr_t address)
{
RISCV013_INFO(info);
unsigned sbasize = get_field(info->sbcs, DMI_SBCS_SBASIZE);
/* There currently is no support for >64-bit addresses in OpenOCD. */
if (sbasize > 96) {
dmi_write(target, DMI_SBADDRESS3, 0);
}
if (sbasize > 64) {
dmi_write(target, DMI_SBADDRESS2, 0);
}
if (sbasize > 32) {
#if BUILD_TARGET64
dmi_write(target, DMI_SBADDRESS1, address >> 32);
#else
dmi_write(target, DMI_SBADDRESS1, 0);
#endif
}
return dmi_write(target, DMI_SBADDRESS0, address);
}
/**
* Read the requested memory using the system bus interface.
*/
static int read_memory_bus(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
uint32_t sbcs = set_field(0, DMI_SBCS_SBREADONADDR, 1);
sbcs |= sb_sbaccess(size);
sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, count > 1);
dmi_write(target, DMI_SBCS, sbcs);
/* This address write will trigger the first read. */
sb_write_address(target, address);
for (uint32_t i = 0; i < count - 1; i++) {
read_memory_bus_word(target, size, buffer + i * size);
}
sbcs = set_field(sbcs, DMI_SBCS_SBREADONDATA, 0);
dmi_write(target, DMI_SBCS, sbcs);
read_memory_bus_word(target, size, buffer + (count - 1) * size);
sbcs = dmi_read(target, DMI_SBCS);
unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
if (error == 0) {
return ERROR_OK;
} else if (error == 1 || error == 2 || error == 3) {
/* Bus timeout, bus error, other bus error. */
return ERROR_FAIL;
} else if (error == 4) {
assert(0);
/* TODO: Reading too fast. We should deal with this properly. */
}
return ERROR_FAIL;
}
/**
* Read the requested memory, taking care to execute every read exactly once,
* even if cmderr=busy is encountered.
*/
static int read_memory(struct target *target, target_addr_t address,
static int read_memory_progbuf(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
RISCV013_INFO(info);
@ -1679,7 +1786,76 @@ error:
return result;
}
static int write_memory(struct target *target, target_addr_t address,
static int read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
RISCV013_INFO(info);
if ((get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1) && (
(get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16))) {
return read_memory_bus(target, address, size, count, buffer);
} else {
return read_memory_progbuf(target, address, size, count, buffer);
}
}
static int write_memory_bus(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
uint32_t sbcs = sb_sbaccess(size);
sbcs = set_field(sbcs, DMI_SBCS_SBAUTOINCREMENT, 1);
dmi_write(target, DMI_SBCS, sbcs);
sb_write_address(target, address);
for (uint32_t i = 0; i < count; i++) {
const uint8_t *p = buffer + i * size;
if (size > 12)
dmi_write(target, DMI_SBDATA3,
((uint32_t) p[12]) |
(((uint32_t) p[13]) << 8) |
(((uint32_t) p[14]) << 16) |
(((uint32_t) p[15]) << 24));
if (size > 8)
dmi_write(target, DMI_SBDATA2,
((uint32_t) p[8]) |
(((uint32_t) p[9]) << 8) |
(((uint32_t) p[10]) << 16) |
(((uint32_t) p[11]) << 24));
if (size > 4)
dmi_write(target, DMI_SBDATA1,
((uint32_t) p[4]) |
(((uint32_t) p[5]) << 8) |
(((uint32_t) p[6]) << 16) |
(((uint32_t) p[7]) << 24));
uint32_t value = p[0];
if (size > 2) {
value |= ((uint32_t) p[2]) << 16;
value |= ((uint32_t) p[3]) << 24;
}
if (size > 1)
value |= ((uint32_t) p[1]) << 8;
dmi_write(target, DMI_SBDATA0, value);
}
sbcs = dmi_read(target, DMI_SBCS);
unsigned error = get_field(sbcs, DMI_SBCS_SBERROR);
if (error == 0) {
return ERROR_OK;
} else if (error == 1 || error == 2 || error == 3) {
/* Bus timeout, bus error, other bus error. */
return ERROR_FAIL;
} else if (error == 4) {
assert(0);
/* TODO: Reading too fast. We should deal with this properly. */
}
return ERROR_FAIL;
}
static int write_memory_progbuf(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
RISCV013_INFO(info);
@ -1856,6 +2032,22 @@ error:
return result;
}
static int write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
RISCV013_INFO(info);
if ((get_field(info->sbcs, DMI_SBCS_SBVERSION) == 1) && (
(get_field(info->sbcs, DMI_SBCS_SBACCESS8) && size == 1) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS16) && size == 2) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS32) && size == 4) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS64) && size == 8) ||
(get_field(info->sbcs, DMI_SBCS_SBACCESS128) && size == 16))) {
return write_memory_bus(target, address, size, count, buffer);
} else {
return write_memory_progbuf(target, address, size, count, buffer);
}
}
static int arch_state(struct target *target)
{
return ERROR_OK;