From 0b08aec9f2f3cf78dc01d2d6a1a68214ff5afcf1 Mon Sep 17 00:00:00 2001 From: Rafael Silva Date: Fri, 15 Dec 2023 17:44:16 +0000 Subject: [PATCH] !experimental! DO NOT MERGE --- src/target/riscv32.c | 303 ++++++++++++++++++++++------------- src/target/riscv64.c | 4 +- src/target/riscv_debug.c | 152 ++++++++++++------ src/target/riscv_debug.h | 81 +++++++--- src/target/target_internal.h | 2 +- 5 files changed, 356 insertions(+), 186 deletions(-) diff --git a/src/target/riscv32.c b/src/target/riscv32.c index 97af7aba64d..50db08ee6a1 100644 --- a/src/target/riscv32.c +++ b/src/target/riscv32.c @@ -72,6 +72,8 @@ static ssize_t riscv32_reg_read(target_s *target, uint32_t c, void *data, size_t static ssize_t riscv32_reg_write(target_s *target, uint32_t c, const void *data, size_t max); static void riscv32_regs_read(target_s *target, void *data); static void riscv32_regs_write(target_s *target, const void *data); +static void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); +static void riscv32_mem_write(target_s *target, target_addr_t dest, const void *src, size_t len); static int riscv32_breakwatch_set(target_s *target, breakwatch_s *breakwatch); static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); @@ -79,9 +81,7 @@ static int riscv32_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); bool riscv32_probe(target_s *const target) { /* Finish setting up the target structure with generic rv32 functions */ - target->core = "rv32"; - /* Provide the length of a suitable registers structure */ - target->regs_size = sizeof(riscv32_regs_s); + target->regs_size = sizeof(riscv32_regs_s); /* Provide the length of a suitable registers structure */ target->regs_read = riscv32_regs_read; target->regs_write = riscv32_regs_write; target->reg_write = riscv32_reg_write; @@ -221,7 +221,7 @@ uint32_t riscv32_pack_data(const void *const src, const uint8_t access_width) static void riscv32_abstract_mem_read( riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { - /* Figure out the maximal width of access to perform, up to the bitness of the target */ + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ const uint8_t access_width = riscv_mem_access_width(hart, src, len); const uint8_t access_length = 1U << access_width; /* Build the access command */ @@ -285,6 +285,7 @@ static void riscv_sysbus_check(riscv_hart_s *const hart) static void riscv32_sysbus_mem_native_read(riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len, const uint8_t access_width, const uint8_t access_length) { + DEBUG_TARGET("%s: %zu byte read at %08" PRIx32 " in %u byte blocks\n", __func__, len, src, access_length); /* Build the access command */ const uint32_t command = ((uint32_t)access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | RV_SYSBUS_MEM_READ_ON_ADDR | (access_length < len ? RV_SYSBUS_MEM_ADDR_POST_INC | RV_SYSBUS_MEM_READ_ON_DATA : 0U); @@ -318,38 +319,27 @@ static void riscv32_sysbus_mem_native_read(riscv_hart_s *const hart, void *const } static void riscv32_sysbus_mem_adjusted_read(riscv_hart_s *const hart, void *const dest, const target_addr_t src, - const uint8_t access_length, const uint8_t access_width, const uint8_t native_access_length) + const uint8_t access_width, const uint8_t native_access_width, const uint8_t access_length) { - const target_addr_t alignment = ~(native_access_length - 1U); + DEBUG_PROBE("%s: %u read for %u bytes\n", __func__, access_length, 1U << access_width); + const target_addr_t alignment = ~(access_length - 1U); /* * On a 32-bit target the only possible widths are 8- 16- and 32-bit, so after the adjustment loop, * there are only and exactly 2 possible cases to handle here: 16- and 32-bit access. */ - switch (access_width) { + switch (native_access_width) { case RV_MEM_ACCESS_16_BIT: { uint16_t value = 0; - /* Run the 16-bit native read, storing the result in `value` */ riscv32_sysbus_mem_native_read( - hart, &value, src & alignment, native_access_length, RV_MEM_ACCESS_16_BIT, native_access_length); - /* Having completed the read, unpack the data (we only care about a single byte in the access) */ - adiv5_unpack_data(dest, src, value, ALIGN_8BIT); + hart, &value, src & alignment, access_length, RV_MEM_ACCESS_16_BIT, access_length); + adiv5_unpack_data(dest, src & 1U, value, access_width); break; } case RV_MEM_ACCESS_32_BIT: { uint32_t value = 0; - /* Run the 32-bit native read, storing the result in `value` */ riscv32_sysbus_mem_native_read( - hart, &value, src & alignment, native_access_length, RV_MEM_ACCESS_32_BIT, native_access_length); - - char *data = (char *)dest; - /* Figure out from the access length the initial unpack and adjustment */ - const uint8_t adjustment = access_length & (uint8_t)~1U; - /* Having completed the read, unpack the first part of the data (two bytes) */ - if (adjustment) - data = (char *)adiv5_unpack_data(data, src, value, ALIGN_16BIT); - /* Now unpack the remaining byte if necessary */ - if (access_length & 1U) - adiv5_unpack_data(data, src + adjustment, value, ALIGN_8BIT); + hart, &value, src & alignment, access_length, RV_MEM_ACCESS_32_BIT, access_length); + adiv5_unpack_data(dest, src & 3U, value, access_width); break; } } @@ -373,25 +363,16 @@ static void riscv32_sysbus_mem_read( ++native_access_width; const uint8_t native_access_length = (uint8_t)(1U << native_access_width); - /* Figure out how much the length is getting adjusted by in the first read to make it aligned */ - const target_addr_t length_adjustment = src & (native_access_length - 1U); - /* - * Having done this, figure out how long the resulting read actually is so we can fill enough of the - * destination buffer with a single read - */ - const uint8_t read_length = - len + length_adjustment <= native_access_length ? len : native_access_length - length_adjustment; - /* Do the initial adjusted access */ size_t remainder = len; target_addr_t address = src; uint8_t *data = (uint8_t *)dest; - riscv32_sysbus_mem_adjusted_read(hart, data, address, read_length, native_access_width, native_access_length); + riscv32_sysbus_mem_adjusted_read(hart, data, address, access_width, native_access_width, native_access_length); /* After doing the initial access, adjust the location of the next and do any follow-up accesses required */ - remainder -= read_length; - address += read_length; - data += read_length; + remainder -= access_length; + address += access_length; + data += access_length; /* * Now we're aligned to the wider access width, do another set of reads if there's @@ -408,12 +389,13 @@ static void riscv32_sysbus_mem_read( /* If there's any data left to read, do another adjusted access to grab it */ if (remainder) - riscv32_sysbus_mem_adjusted_read(hart, data, address, remainder, native_access_width, native_access_length); + riscv32_sysbus_mem_adjusted_read(hart, data, address, access_width, native_access_width, native_access_length); } static void riscv32_sysbus_mem_native_write(riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len, const uint8_t access_width, const uint8_t access_length) { + DEBUG_TARGET("%s: %zu byte write at %08" PRIx32 " in %u byte blocks\n", __func__, len, dest, access_length); /* Build the access command */ const uint32_t command = ((uint32_t)access_width << RV_SYSBUS_MEM_ACCESS_SHIFT) | (access_length < len ? RV_SYSBUS_MEM_ADDR_POST_INC : 0U); @@ -442,9 +424,10 @@ static void riscv32_sysbus_mem_native_write(riscv_hart_s *const hart, const targ } static void riscv32_sysbus_mem_adjusted_write(riscv_hart_s *const hart, const target_addr_t dest, const void *const src, - const uint8_t access_length, const uint8_t access_width, const uint8_t native_access_length) + const uint8_t access_width, const uint8_t native_access_width, const uint8_t access_length) { - const target_addr_t alignment = ~(native_access_length - 1U); + DEBUG_PROBE("%s: %u write for %u bytes\n", __func__, access_length, 1U << access_width); + const target_addr_t alignment = ~(access_length - 1U); /* * On a 32-bit target the only possible widths are 8- 16- and 32-bit, so after the adjustment loop, * there are only and exactly 2 possible cases to handle here: 16- and 32-bit access. @@ -452,43 +435,31 @@ static void riscv32_sysbus_mem_adjusted_write(riscv_hart_s *const hart, const ta * wider access primitive, we first have to read back what's at the target aligned location, replace * the correct set of bits in the target value, then write the new combined value back */ - switch (access_width) { + switch (native_access_width) { case RV_MEM_ACCESS_16_BIT: { uint16_t value = 0; /* Start by reading 16 bits */ riscv32_sysbus_mem_native_read( - hart, &value, dest & alignment, native_access_length, RV_MEM_ACCESS_16_BIT, native_access_length); + hart, &value, dest & alignment, access_length, RV_MEM_ACCESS_16_BIT, access_length); /* Now replace the part to write (must be done on the widened version of the value) */ uint32_t widened_value = value; - /* - * Note that to get here we're doing a 2 byte access for 1 byte so we only care about a single byte - * replacement. We also have to constrain the replacement to only happen in the lower 16 bits. - */ - adiv5_pack_data(dest & ~2U, src, &widened_value, ALIGN_8BIT); + adiv5_pack_data(dest & 1U, src, &widened_value, access_width); value = (uint16_t)widened_value; /* And finally write the new value back */ riscv32_sysbus_mem_native_write( - hart, dest & alignment, &value, native_access_length, RV_MEM_ACCESS_16_BIT, native_access_length); + hart, dest & alignment, &value, access_length, RV_MEM_ACCESS_16_BIT, access_length); break; } case RV_MEM_ACCESS_32_BIT: { uint32_t value = 0; /* Start by reading 32 bits */ riscv32_sysbus_mem_native_read( - hart, &value, dest & alignment, native_access_length, RV_MEM_ACCESS_32_BIT, native_access_length); - + hart, &value, dest & alignment, access_length, RV_MEM_ACCESS_32_BIT, access_length); /* Now replace the part to write */ - const char *data = (const char *)src; - /* Figure out from the access length the initial pack and adjustment */ - const uint8_t adjustment = access_length & (uint8_t)~1U; - if (adjustment) - data = (const char *)adiv5_pack_data(dest, data, &value, ALIGN_16BIT); - /* Now pack the remaining byte if necessary */ - if (access_length & 1) - adiv5_pack_data(dest + adjustment, data, &value, ALIGN_8BIT); + adiv5_pack_data(dest & 3U, src, &value, access_width); /* And finally write the new value back */ riscv32_sysbus_mem_native_write( - hart, dest & alignment, &value, native_access_length, RV_MEM_ACCESS_32_BIT, native_access_length); + hart, dest & alignment, &value, access_length, RV_MEM_ACCESS_32_BIT, access_length); break; } } @@ -512,25 +483,16 @@ static void riscv32_sysbus_mem_write( ++native_access_width; const uint8_t native_access_length = (uint8_t)(1U << native_access_width); - /* Figure out how much the length is getting adjusted by in the first write to make it aligned */ - const target_addr_t length_adjustment = dest & (native_access_length - 1U); - /* - * Having done this, figure out how long the resulting write actually is so we can fill enough of the - * destination buffer with a single write - */ - const uint8_t write_length = - len + length_adjustment <= native_access_length ? len : native_access_length - length_adjustment; - /* Do the initial adjusted access */ size_t remainder = len; target_addr_t address = dest; const uint8_t *data = (const uint8_t *)src; - riscv32_sysbus_mem_adjusted_write(hart, address, data, write_length, native_access_width, native_access_length); + riscv32_sysbus_mem_adjusted_write(hart, address, data, access_width, native_access_width, native_access_length); /* After doing the initial access, adjust the location of the next and do any follow-up accesses required */ - remainder -= write_length; - address += write_length; - data += write_length; + remainder -= access_length; + address += access_length; + data += access_length; /* * Now we're aligned to the wider access width, do another set of writes if there's @@ -547,64 +509,187 @@ static void riscv32_sysbus_mem_write( /* If there's any data left to write, do another adjusted access to perform it */ if (remainder) - riscv32_sysbus_mem_adjusted_write(hart, address, data, remainder, native_access_width, native_access_length); + riscv32_sysbus_mem_adjusted_write(hart, address, data, access_width, native_access_width, native_access_length); } -void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +static void riscv32_abstract_progbuf_mem_read( + riscv_hart_s *const hart, void *const dest, const target_addr_t src, const size_t len) { - /* If we're asked to do a 0-byte read, do nothing */ - if (!len) { - DEBUG_PROTO("%s: @ %08" PRIx32 " len %zu\n", __func__, src, len); + if (!(hart->extensions & RV_ISA_EXT_COMPRESSED)) { + DEBUG_ERROR("This target does not implement the compressed ISA extension\n"); + return; + } + + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 " using PROGBUF\n", len, src); + + /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + const uint8_t access_width = riscv_mem_access_width(hart, src, len); + // const uint8_t access_length = 1U << access_width; + // /* Build the access command */ + // const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_READ | (access_width << RV_ABST_MEM_ACCESS_SHIFT) | + // (access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U); + // /* Write the address to read to arg1 */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) + // return; + // uint8_t *const data = (uint8_t *)dest; + // for (size_t offset = 0; offset < len; offset += access_length) { + // /* Execute the read */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + // return; + // /* Extract back the data from arg0 */ + // uint32_t value = 0; + // if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) + // return; + // riscv32_unpack_data(data + offset, value, access_width); + // } + + /* Disable auto-exec */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_AUTO, 0)) + // return; + + /* + * progbuf 0 + * c.lw x8,0(x11) // Pull the address from DATA1 + * c.lw x9,0(x8) // Read the data at that location + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF0, 0x40044180U)) + return; + + /* + * progbuf 1 + * c.nop // alternately, `c.addi x8, 4` , for auto-increment (0xc1040411) + * c.sw x9, 0(x10) // Write back to DATA0 + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF1, 0xc1040001U)) + return; + + /* + * progbuf 2 + * c.sw x8, 0(x11) // Write addy to DATA1 + * c.ebreak + */ + if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF2, 0x9002c180U)) + return; + + /* StaticUpdatePROGBUFRegs */ + uint32_t rr; + if (!riscv_dm_read(hart->dbg_module, 0x12, &rr)) { + DEBUG_ERROR("Could not get hart info\n"); return; } + DEBUG_INFO("rr: %08" PRIx32 "\n", rr); + const uint32_t data0_offset = 0xe0000000U | (rr & 0x7ffU); + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, data0_offset)) // DATA0's location in memory. + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100aU)) // Copy data to x10 + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, data0_offset + 4U)) // DATA1's location in memory. + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100bU)) // Copy data to x11 + return; + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, 0x40022010U)) // FLASH->CTLR + // return; + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100cU)) // Copy data to x12 + // return; + // #define CR_PAGE_PG ((uint32_t)0x00010000) + // #define CR_BUF_LOAD ((uint32_t)0x00040000) + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, CR_PAGE_PG | CR_BUF_LOAD)) + // return; + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x0023100dU)) // Copy data to x13 + // return; + + /* Enable auto-exec */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_AUTO, 1U)) + // return; + + if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, src)) + return; + if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, 0x00241000U) || !riscv_command_wait_complete(hart)) + return; + + /* Extract back the data from arg0 */ + uint32_t value = 0; + if (!riscv_dm_read(hart->dbg_module, RV_DM_DATA0, &value)) + return; + + riscv32_unpack_data(dest, value, access_width); +} + +static void riscv32_abstract_progbuf_mem_write( + riscv_hart_s *const hart, const target_addr_t dest, const void *const src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 " using PROGBUF\n", len, dest); + + (void)hart; + (void)dest; + (void)src; + (void)len; + + // /* Figure out the maxmial width of access to perform, up to the bitness of the target */ + // const uint8_t access_width = riscv_mem_access_width(hart, dest, len); + // const uint8_t access_length = 1U << access_width; + // /* Build the access command */ + // const uint32_t command = RV_DM_ABST_CMD_ACCESS_MEM | RV_ABST_WRITE | (access_width << RV_ABST_MEM_ACCESS_SHIFT) | + // (access_length < len ? RV_ABST_MEM_ADDR_POST_INC : 0U); + // /* Write the address to write to arg1 */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA1, dest)) + // return; + // const uint8_t *const data = (const uint8_t *)src; + // for (size_t offset = 0; offset < len; offset += access_length) { + // /* Pack the data to write into arg0 */ + // uint32_t value = riscv32_pack_data(data + offset, access_width); + // if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value)) + // return; + // /* Execute the write */ + // if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND, command) || !riscv_command_wait_complete(hart)) + // return; + // } +} + +static void riscv32_mem_read(target_s *const target, void *const dest, const target_addr_t src, const size_t len) +{ + DEBUG_TARGET("Performing %zu byte read of %08" PRIx32 "\n", len, src); + /* If we're asked to do a 0-byte read, do nothing */ + if (!len) + return; riscv_hart_s *const hart = riscv_hart_struct(target); - if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) + if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) { riscv32_sysbus_mem_read(hart, dest, src, len); - else + return; + } + if (hart->flags & RV_HART_FLAG_MEMORY_ABSTRACT) { riscv32_abstract_mem_read(hart, dest, src, len); - -#if ENABLE_DEBUG - DEBUG_PROTO("%s: @ %08" PRIx32 " len %zu:", __func__, src, len); -#ifndef DEBUG_PROTO_IS_NOOP - const uint8_t *const data = (const uint8_t *)dest; -#endif - for (size_t offset = 0; offset < len; ++offset) { - if (offset == 16U) - break; - DEBUG_PROTO(" %02x", data[offset]); + if (hart->status == RISCV_HART_NOT_SUPP) { + DEBUG_WARN("Abstract memory access not supported, falling back to prog buffer\n"); + hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_ABSTRACT; + } else + return; } - if (len > 16U) - DEBUG_PROTO(" ..."); - DEBUG_PROTO("\n"); -#endif + riscv32_abstract_progbuf_mem_read(hart, dest, src, len); } -void riscv32_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) +static void riscv32_mem_write(target_s *const target, const target_addr_t dest, const void *const src, const size_t len) { -#if ENABLE_DEBUG - DEBUG_PROTO("%s: @ %" PRIx32 " len %zu:", __func__, dest, len); -#ifndef DEBUG_PROTO_IS_NOOP - const uint8_t *const data = (const uint8_t *)src; -#endif - for (size_t offset = 0; offset < len; ++offset) { - if (offset == 16U) - break; - DEBUG_PROTO(" %02x", data[offset]); - } - if (len > 16U) - DEBUG_PROTO(" ..."); - DEBUG_PROTO("\n"); -#endif + DEBUG_TARGET("Performing %zu byte write of %08" PRIx32 "\n", len, dest); /* If we're asked to do a 0-byte read, do nothing */ if (!len) return; riscv_hart_s *const hart = riscv_hart_struct(target); - if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) + if (hart->flags & RV_HART_FLAG_MEMORY_SYSBUS) { riscv32_sysbus_mem_write(hart, dest, src, len); - else + return; + } + if (hart->flags & RV_HART_FLAG_MEMORY_ABSTRACT) { riscv32_abstract_mem_write(hart, dest, src, len); + if (hart->status == RISCV_HART_NOT_SUPP) { + DEBUG_WARN("Abstract memory access not supported, falling back to prog buffer\n"); + hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_ABSTRACT; + } else + return; + } + riscv32_abstract_progbuf_mem_write(hart, dest, src, len); } /* diff --git a/src/target/riscv64.c b/src/target/riscv64.c index 2e25bd17b42..19eadb8681f 100644 --- a/src/target/riscv64.c +++ b/src/target/riscv64.c @@ -49,9 +49,7 @@ static void riscv64_mem_read(target_s *target, void *dest, target_addr_t src, si bool riscv64_probe(target_s *const target) { /* Finish setting up the target structure with generic rv64 functions */ - target->core = "rv64"; - /* Provide the length of a suitable registers structure */ - target->regs_size = sizeof(riscv64_regs_s); + target->regs_size = sizeof(riscv64_regs_s); /* Provide the length of a suitable registers structure */ target->regs_read = riscv64_regs_read; target->regs_write = riscv64_regs_write; target->mem_read = riscv64_mem_read; diff --git a/src/target/riscv_debug.c b/src/target/riscv_debug.c index 99240b20ed2..9a9dd256b31 100644 --- a/src/target/riscv_debug.c +++ b/src/target/riscv_debug.c @@ -113,8 +113,8 @@ #define RV_GPRS_COUNT 32U -/* This enum defines the set of currently known and valid halt causes */ -typedef enum riscv_halt_cause { + /* This enum defines the set of currently known and valid halt causes */ + typedef enum riscv_halt_cause { /* Halt was caused by an `ebreak` instruction executing */ RV_HALT_CAUSE_EBREAK = (1U << 6U), /* Halt was caused by a breakpoint or watchpoint (set in the trigger module) */ @@ -220,9 +220,6 @@ static void riscv_detach(target_s *target); static const char *riscv_target_description(target_s *target); static bool riscv_check_error(target_s *target); -static void riscv_halt_request(target_s *target); -static void riscv_halt_resume(target_s *target, bool step); -static target_halt_reason_e riscv_halt_poll(target_s *target, target_addr_t *watch); static void riscv_reset(target_s *target); void riscv_dmi_init(riscv_dmi_s *const dmi) @@ -350,6 +347,64 @@ static void riscv_hart_read_ids(riscv_hart_s *const hart) /* rv128 is unimpl. */ } +static size_t append_char(char *const buffer, const size_t offset, const size_t max_length, const char c) +{ + if (buffer && offset < max_length) + buffer[offset] = c; + return offset + 1; +} + +static size_t riscv_snprint_isa_subset(const riscv_hart_s *const hart, char *const buffer, const size_t max_length) +{ + size_t offset = snprintf(buffer, max_length, "rv%" PRIu8, hart->access_width); + + const bool is_embedded = hart->extensions & RV_ISA_EXT_EMBEDDED; + + offset = append_char(buffer, offset, max_length, is_embedded ? 'e' : 'i'); + + const bool is_general_purpose_isa = + !is_embedded && (hart->extensions & RV_ISA_EXT_GENERAL_PURPOSE) == RV_ISA_EXT_GENERAL_PURPOSE; + + if (is_general_purpose_isa) { + offset = append_char(buffer, offset, max_length, 'g'); + if (hart->extensions & RV_ISA_EXT_QUAD_FLOAT) + offset = append_char(buffer, offset, max_length, 'q'); + } else { + if (hart->extensions & RV_ISA_EXT_MUL_DIV_INT) + offset = append_char(buffer, offset, max_length, 'm'); + if (hart->extensions & RV_ISA_EXT_ATOMIC) + offset = append_char(buffer, offset, max_length, 'a'); + if (hart->extensions & RV_ISA_EXT_QUAD_FLOAT) + offset = append_char(buffer, offset, max_length, 'q'); /* Implies d */ + else if (hart->extensions & RV_ISA_EXT_DOUBLE_FLOAT) + offset = append_char(buffer, offset, max_length, 'd'); /* Implies f */ + else if (hart->extensions & RV_ISA_EXT_SINGLE_FLOAT) + offset = append_char(buffer, offset, max_length, 'f'); + } + if (hart->extensions & RV_ISA_EXT_DECIMAL_FLOAT) + offset = append_char(buffer, offset, max_length, 'l'); + if (hart->extensions & RV_ISA_EXT_COMPRESSED) + offset = append_char(buffer, offset, max_length, 'c'); + if (hart->extensions & RV_ISA_EXT_BIT_MANIP) + offset = append_char(buffer, offset, max_length, 'b'); + if (hart->extensions & RV_ISA_EXT_DYNAMIC_LANG) + offset = append_char(buffer, offset, max_length, 'j'); + if (hart->extensions & RV_ISA_EXT_TRANSACT_MEM) + offset = append_char(buffer, offset, max_length, 't'); + if (hart->extensions & RV_ISA_EXT_PACKED_SIMD) + offset = append_char(buffer, offset, max_length, 'p'); + if (hart->extensions & RV_ISA_EXT_VECTOR) + offset = append_char(buffer, offset, max_length, 'v'); + if (hart->extensions & RV_ISA_EXT_USER_INTERRUPTS) + offset = append_char(buffer, offset, max_length, 'n'); + + /* null-terminate the string */ + if (max_length > 0) + buffer[offset < max_length ? offset : max_length - 1] = '\0'; + + return offset; +} + static bool riscv_hart_init(riscv_hart_s *const hart) { /* Allocate a new target */ @@ -368,9 +423,9 @@ static bool riscv_hart_init(riscv_hart_s *const hart) uint32_t isa = riscv_hart_discover_isa(hart); hart->address_width = riscv_isa_address_width(isa); hart->extensions = isa & RV_ISA_EXTENSIONS_MASK; - /* Figure out if the target needs us to use sysbus or not for memory access */ - riscv_hart_memory_access_type(hart); - /* Then read out the ID registers */ + static char core_string[32U]; // FIXME: Extremely hacky, do not merge! + riscv_snprint_isa_subset(hart, core_string, sizeof(core_string)); + target->core = core_string; riscv_hart_read_ids(hart); DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), vendor = %" PRIx32 ", impl = %" PRIx32 @@ -379,7 +434,6 @@ static bool riscv_hart_init(riscv_hart_s *const hart) /* We don't support rv128, so tell the user and fast-quit on this target. */ if (hart->access_width == 128U) { - target->core = "(unsup) rv128"; DEBUG_WARN("rv128 is unsupported, ignoring this hart\n"); return true; } @@ -388,8 +442,8 @@ static bool riscv_hart_init(riscv_hart_s *const hart) target->designer_code = hart->vendorid ? hart->vendorid : hart->dbg_module->dmi_bus->designer_code; target->cpuid = hart->archid; - /* Now we're in a safe environment, leasurely read out the triggers, etc. */ riscv_hart_discover_triggers(hart); + riscv_hart_memory_access_type(hart); /* Setup core-agnostic target functions */ target->attach = riscv_attach; @@ -436,7 +490,7 @@ static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint3 } while (dmi->fault == RV_DMI_TOO_SOON); if (result) - DEBUG_PROTO("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value); + DEBUG_TARGET("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value); else DEBUG_WARN("%s: %08" PRIx32 " failed: %u\n", __func__, address, dmi->fault); return result; @@ -444,7 +498,7 @@ static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint3 static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value) { - DEBUG_PROTO("%s: %08" PRIx32 " <- %08" PRIx32 "\n", __func__, address, value); + DEBUG_TARGET("%s: %08" PRIx32 " <- %08" PRIx32 "\n", __func__, address, value); bool result = false; do { @@ -690,11 +744,7 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger); /* Try reading the trigger info */ uint32_t info = 0; - /* - * If the read succeeds but info is still 0, assume we're talking to something like a WCH device - * which'll do this despite not actually implementing the tinfo register. Handle it the same as - * the read explicitly failing. - */ + /* Some chips reply ok but return 0 (WCH)*/ if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info) || !info) { /* * If that fails, it's probably because the tinfo register isn't implemented, so read @@ -724,17 +774,23 @@ static void riscv_hart_discover_triggers(riscv_hart_s *const hart) static void riscv_hart_memory_access_type(riscv_hart_s *const hart) { - uint32_t sysbus_status; - hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_SYSBUS; + hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_MASK; /* * Try reading the system bus access control and status register. * Check if the value read back is non-zero for the sbasize field */ - if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &sysbus_status) || - !(sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK)) - return; - /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ - hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); + uint32_t sysbus_status; + if (riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &sysbus_status) && + (sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK)) { + /* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */ + hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK); + } else { + /* + * If the system bus is not valid, we need to fall back to using abstract commands + * Later, if the memory access fails, we'll clear the flag and fall back to use the prog buffer + */ + hart->flags = RV_HART_FLAG_MEMORY_ABSTRACT; + } /* Make sure the system bus is not in any kind of error state */ (void)riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, 0x00407000U); } @@ -824,7 +880,7 @@ static bool riscv_dm_poll_state(riscv_dm_s *const dbg_module, const uint32_t sta return true; } -static void riscv_halt_request(target_s *const target) +void riscv_halt_request(target_s *const target) { riscv_hart_s *const hart = riscv_hart_struct(target); /* Request the hart to halt */ @@ -837,7 +893,7 @@ static void riscv_halt_request(target_s *const target) (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } -static void riscv_halt_resume(target_s *target, const bool step) +void riscv_halt_resume(target_s *const target, const bool step) { riscv_hart_s *const hart = riscv_hart_struct(target); /* Configure the debug controller for single-stepping as appropriate */ @@ -860,7 +916,7 @@ static void riscv_halt_resume(target_s *target, const bool step) (void)riscv_dm_write(hart->dbg_module, RV_DM_CONTROL, hart->hartsel); } -static target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_t *const watch) +target_halt_reason_e riscv_halt_poll(target_s *const target, target_addr_t *const watch) { (void)watch; riscv_hart_s *const hart = riscv_hart_struct(target); @@ -895,7 +951,7 @@ static void riscv_reset(target_s *const target) riscv_hart_s *const hart = riscv_hart_struct(target); bool has_reset = false; /* If the target does not have the nRST pin inhibited, use that to initiate reset */ - if (!(target->target_options & TOPT_INHIBIT_NRST)) { + if (!(target->target_options & RV_TOPT_INHIBIT_NRST)) { platform_nrst_set_val(true); has_reset = riscv_dm_poll_state(hart->dbg_module, RV_DM_STAT_ALL_RESET); platform_nrst_set_val(false); @@ -918,17 +974,6 @@ static void riscv_reset(target_s *const target) target_check_error(target); } -static const char *riscv_fpu_ext_string(const uint32_t extensions) -{ - if (extensions & RV_ISA_EXT_QUAD_FLOAT) - return "q"; - if (extensions & RV_ISA_EXT_DOUBLE_FLOAT) - return "d"; - if (extensions & RV_ISA_EXT_SINGLE_FLOAT) - return "f"; - return ""; -} - /* * This function creates the target description XML string for a RISC-V part. * This is done this way to decrease string duplication and thus code size, making it @@ -979,16 +1024,20 @@ static const char *riscv_fpu_ext_string(const uint32_t extensions) * "" */ static size_t riscv_build_target_description( - char *const buffer, size_t max_length, const uint8_t address_width, const uint32_t extensions) + char *const buffer, const size_t max_length, const riscv_hart_s *const hart) { - const bool embedded = extensions & RV_ISA_EXT_EMBEDDED; - const uint32_t fpu = extensions & RV_ISA_EXT_ANY_FLOAT; + const bool embedded = hart->extensions & RV_ISA_EXT_EMBEDDED; size_t print_size = max_length; /* Start with the "preamble" chunks, which are mostly common across targets save for 2 words. */ - int offset = snprintf(buffer, print_size, "%s target %sriscv:rv%u%c%s%s ", - gdb_xml_preamble_first, gdb_xml_preamble_second, address_width, embedded ? 'e' : 'i', riscv_fpu_ext_string(fpu), - gdb_xml_preamble_third); + int offset = snprintf(buffer, print_size, "%s target %sriscv:", gdb_xml_preamble_first, gdb_xml_preamble_second); + + if (max_length != 0) + print_size = max_length - (size_t)offset; + + offset += riscv_snprint_isa_subset(hart, buffer + offset, print_size); + + offset += snprintf(buffer, print_size, "%s ", gdb_xml_preamble_third); const uint8_t gprs = embedded ? 16U : 32U; /* Then build the general purpose register descriptions using the arrays at top of file */ @@ -1000,14 +1049,14 @@ static size_t riscv_build_target_description( const char *const name = riscv_gpr_names[i]; const gdb_reg_type_e type = riscv_gpr_types[i]; - offset += snprintf(buffer + offset, print_size, "", name, address_width, - gdb_reg_type_strings[type], i == 0 ? " regnum=\"0\"" : ""); + offset += snprintf(buffer + offset, print_size, "", name, + hart->address_width, gdb_reg_type_strings[type], i == 0 ? " regnum=\"0\"" : ""); } /* Then build the program counter register description, which has the same bitsize as the GPRs. */ if (max_length != 0) print_size = max_length - (size_t)offset; - offset += snprintf(buffer + offset, print_size, "", address_width, + offset += snprintf(buffer + offset, print_size, "", hart->address_width, gdb_reg_type_strings[GDB_TYPE_CODE_PTR]); /* XXX: TODO - implement generation of the FPU feature and registers */ @@ -1020,7 +1069,7 @@ static size_t riscv_build_target_description( if (max_length != 0) print_size = max_length - (size_t)offset; offset += snprintf(buffer + offset, print_size, " ", - riscv_csrs[i].name, address_width, riscv_csrs[i].csr_number + RV_CSR_GDB_OFFSET, + riscv_csrs[i].name, hart->address_width, riscv_csrs[i].csr_number + RV_CSR_GDB_OFFSET, gdb_reg_save_restore_strings[GDB_SAVE_RESTORE_NO]); } /* Add the closing tags required */ @@ -1035,10 +1084,9 @@ static size_t riscv_build_target_description( static const char *riscv_target_description(target_s *const target) { const riscv_hart_s *const hart = riscv_hart_struct(target); - const size_t description_length = - riscv_build_target_description(NULL, 0, hart->address_width, hart->extensions) + 1U; + const size_t description_length = riscv_build_target_description(NULL, 0, hart) + 1U; char *const description = malloc(description_length); if (description) - (void)riscv_build_target_description(description, description_length, hart->address_width, hart->extensions); + (void)riscv_build_target_description(description, description_length, hart); return description; } diff --git a/src/target/riscv_debug.h b/src/target/riscv_debug.h index 1af7c72b334..63154d408a6 100644 --- a/src/target/riscv_debug.h +++ b/src/target/riscv_debug.h @@ -85,8 +85,9 @@ typedef enum riscv_match_size { } riscv_match_size_e; /* These defines specify Hart-specific information such as which memory access style to use */ -#define RV_HART_FLAG_MEMORY_ABSTRACT 0x00U +#define RV_HART_FLAG_MEMORY_MASK 0x30U #define RV_HART_FLAG_MEMORY_SYSBUS 0x10U +#define RV_HART_FLAG_MEMORY_ABSTRACT 0x20U #define RV_HART_FLAG_ACCESS_WIDTH_MASK 0x0fU #define RV_HART_FLAG_ACCESS_WIDTH_8BIT 0x01U #define RV_HART_FLAG_ACCESS_WIDTH_16BIT 0x02U @@ -159,6 +160,10 @@ typedef struct riscv_hart { #define RV_DM_DATA3 0x07U #define RV_DM_ABST_CTRLSTATUS 0x16U #define RV_DM_ABST_COMMAND 0x17U +#define RV_DM_ABST_AUTO 0x18U +#define RV_DM_PROGBUF0 0x20U +#define RV_DM_PROGBUF1 0x21U +#define RV_DM_PROGBUF2 0x22U #define RV_DM_SYSBUS_CTRLSTATUS 0x38U #define RV_DM_SYSBUS_ADDR0 0x39U #define RV_DM_SYSBUS_ADDR1 0x3aU @@ -197,29 +202,62 @@ typedef struct riscv_hart { /* The FP base defines the starting register space address for the floating point registers */ #define RV_FP_BASE 0x1020U -#define RV_ISA_EXT_EMBEDDED 0x00000010U -#define RV_ISA_EXT_ANY_FLOAT 0x00010028U -#define RV_ISA_EXT_SINGLE_FLOAT 0x00000020U -#define RV_ISA_EXT_DOUBLE_FLOAT 0x00000008U -#define RV_ISA_EXT_QUAD_FLOAT 0x00010000U +/* + * The Extensions field encodes the presence of standard extensions, single bit per alphabet letter + * (bit 0 encodes presence of extension ā€œAā€ through to bit 25 which encodes ā€œZā€) + * + * This list is taken from the RISC-V Instruction Set Manual v2.2 + * + * The list order is the canonical representation order in the ISA subset string + */ + +/* Base ISA */ +#define RV_ISA_EXT_INTEGER (1U << 8U) /* 'I': RV32I/64I/128I integer base ISA */ +#define RV_ISA_EXT_EMBEDDED (1U << 4U) /* 'E': RV32E reduced integer base ISA (Embedded) */ + +/* Standard general-purpose ISA */ +#define RV_ISA_EXT_MUL_DIV_INT (1U << 12U) /* 'M': Integer multiplication and division */ +#define RV_ISA_EXT_ATOMIC (1U << 0U) /* 'A': Atomic instructions */ +#define RV_ISA_EXT_SINGLE_FLOAT (1U << 5U) /* 'F': Single-precision floating-point */ +#define RV_ISA_EXT_DOUBLE_FLOAT (1U << 3U) /* 'D': Double-precision floating-point */ + +/* 'G' standard general-purpose ISA abreviation, representing 'IMAFD' */ +#define RV_ISA_EXT_GENERAL_PURPOSE \ + (RV_ISA_EXT_INTEGER | RV_ISA_EXT_MUL_DIV_INT | RV_ISA_EXT_ATOMIC | RV_ISA_EXT_SINGLE_FLOAT | \ + RV_ISA_EXT_DOUBLE_FLOAT) + +/* Standard Unprivileged Extensions */ +#define RV_ISA_EXT_QUAD_FLOAT (1U << 16U) /* 'Q': Quad-precision floating-point */ +#define RV_ISA_EXT_DECIMAL_FLOAT (1U << 11U) /* 'L': Decimal floating-point */ +#define RV_ISA_EXT_COMPRESSED (1U << 2U) /* 'C': 16-bit compressed instructions */ +#define RV_ISA_EXT_BIT_MANIP (1U << 1U) /* 'B': Bit manipulation */ +#define RV_ISA_EXT_DYNAMIC_LANG (1U << 9U) /* 'J': Dynamic languages */ +#define RV_ISA_EXT_TRANSACT_MEM (1U << 19U) /* 'T': Transactional memory */ +#define RV_ISA_EXT_PACKED_SIMD (1U << 15U) /* 'P': Packed-SIMD */ +#define RV_ISA_EXT_VECTOR (1U << 21U) /* 'V': Vector extensions */ +#define RV_ISA_EXT_USER_INTERRUPTS (1U << 13U) /* 'N': User-level interrupts */ #define RV_TRIGGER_SUPPORT_MASK 0x0000fffeU #define RV_TRIGGER_MODE_MASK 0xffff0000U #define RV_TRIGGER_SUPPORT_BREAKWATCH 0x00000004U -// The CSR id when reported by GDB is shifted by RV_CSR_GDB_OFFSET -// so they cannot collide with GPR registers, so you have to subtract -// RV_CSR_GDB_OFFSET from the value received from gdb -#define RV_CSR_GDB_OFFSET 128 -#define RV_CSR_STATUS 0x300 -#define RV_CSR_MISA 0x301 -#define RV_CSR_MIE 0x304 -#define RV_CSR_MTVEC 0x305 -#define RV_CSR_MSCRATCH 0x340 -#define RV_CSR_MEPC 0x341 -#define RV_CSR_MCAUSE 0x342 -#define RV_CSR_MTVAL 0x343 -#define RV_CSR_MIP 0x344 +/* + * The CSR id when reported by GDB is shifted by RV_CSR_GDB_OFFSET + * so they cannot collide with GPR registers, so you have to substract + * RV_CSR_GDB_OFFSET from the value received from gdb + */ +#define RV_CSR_GDB_OFFSET 128U +#define RV_CSR_STATUS 0x300U +#define RV_CSR_MISA 0x301U +#define RV_CSR_MIE 0x304U +#define RV_CSR_MTVEC 0x305U +#define RV_CSR_MSCRATCH 0x340U +#define RV_CSR_MEPC 0x341U +#define RV_CSR_MCAUSE 0x342U +#define RV_CSR_MTVAL 0x343U +#define RV_CSR_MIP 0x344U + +#define RV_TOPT_INHIBIT_NRST 0x00000001U void riscv_jtag_dtm_handler(uint8_t dev_index); void riscv_dmi_init(riscv_dmi_s *dmi); @@ -238,7 +276,8 @@ uint8_t riscv_mem_access_width(const riscv_hart_s *hart, target_addr_t address, void riscv32_unpack_data(void *dest, uint32_t data, uint8_t access_width); uint32_t riscv32_pack_data(const void *src, uint8_t access_width); -void riscv32_mem_read(target_s *target, void *dest, target_addr_t src, size_t len); -void riscv32_mem_write(target_s *target, target_addr_t dest, const void *src, size_t len); +void riscv_halt_request(target_s *target); +void riscv_halt_resume(target_s *target, bool step); +target_halt_reason_e riscv_halt_poll(target_s *target, target_addr_t *watch); #endif /*TARGET_RISCV_DEBUG_H*/ diff --git a/src/target/target_internal.h b/src/target/target_internal.h index ff932b954ea..65fc9a3dfae 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -156,7 +156,7 @@ struct target { /* Other stuff */ const char *driver; uint32_t cpuid; - char *core; + const char *core; char cmdline[MAX_CMDLINE]; target_addr_t heapinfo[4]; target_command_s *commands;