This is an automated email from Gerrit. "Name of user not set <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9786
-- gerrit commit faedee0edf4bb469dfbe56dd8f362ae9e2fee117 Author: ImproperCatGirl <[email protected]> Date: Sat Jul 18 23:27:13 2026 +0800 target/riscv: add DTM abstraction and AP backend Introduce a named RISC-V DTM object and route DMI access through backend operations instead of assuming that every RISC-V target owns a JTAG TAP. The legacy JTAG path remains the default and is implemented as a JTAG DTM backend. Add an ADIv5/ADIv6 AP-backed DTM backend so targets such as RP2350 Hazard3 can expose RISC-V DMI through an AP rather than through JTAG. Keep the higher RISC-V debug-module register logic transport-neutral and add a minimal RP2350 RISC-V target configuration as a testable example for CMSIS-DAP/SWD setups. Change-Id: I47c2501023fbbe59f2f0502ff84d1c2106080a5a Signed-off-by: ImproperCatGirl <[email protected]> diff --git a/src/openocd.c b/src/openocd.c index f3e1bee48e..27e9959b96 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -27,6 +27,7 @@ #include <target/arm_cti.h> #include <target/arm_adi_v5.h> #include <target/arm_tpiu_swo.h> +#include <target/riscv/dtm.h> #include <rtt/rtt.h> #include <server/server.h> @@ -147,6 +148,10 @@ COMMAND_HANDLER(handle_init_command) if (retval != ERROR_OK) return ERROR_FAIL; + retval = command_run_line(CMD_CTX, "dtm init"); + if (retval != ERROR_OK) + return ERROR_FAIL; + LOG_DEBUG("Examining targets..."); if (target_examine() != ERROR_OK) LOG_DEBUG("target examination failed"); @@ -247,6 +252,7 @@ static int (* const command_registrants[])(struct command_context *cmd_ctx_value pld_register_commands, cti_register_commands, dap_register_commands, + riscv_dtm_register_commands, arm_tpiu_swo_register_commands, }; @@ -362,6 +368,7 @@ int openocd_main(int argc, char *argv[]) /* free all DAP and CTI objects */ arm_cti_cleanup_all(); dap_cleanup_all(); + riscv_dtm_cleanup_all(); adapter_quit(); diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am index 189edb3e6e..ebe96d73c1 100644 --- a/src/target/riscv/Makefile.am +++ b/src/target/riscv/Makefile.am @@ -5,9 +5,12 @@ noinst_LTLIBRARIES += %D%/libriscv.la %D%/batch.h \ %D%/debug_defines.h \ %D%/debug_reg_printer.h \ + %D%/dmi.h \ + %D%/dtm.h \ %D%/encoding.h \ %D%/field_helpers.h \ %D%/gdb_regs.h \ + %D%/dtm/internal.h \ %D%/opcodes.h \ %D%/program.h \ %D%/riscv.h \ @@ -27,6 +30,10 @@ noinst_LTLIBRARIES += %D%/libriscv.la %D%/riscv_reg.c \ %D%/riscv_semihosting.c \ %D%/debug_defines.c \ - %D%/debug_reg_printer.c + %D%/debug_reg_printer.c \ + %D%/dmi.c \ + %D%/dtm.c \ + %D%/dtm/adi_ap.c \ + %D%/dtm/jtag.c STARTUP_TCL_SRCS += %D%/startup.tcl diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c index 5acbddaf37..d482ab715a 100644 --- a/src/target/riscv/batch.c +++ b/src/target/riscv/batch.c @@ -5,453 +5,113 @@ #endif #include "batch.h" -#include "debug_defines.h" -#include "debug_reg_printer.h" -#include "riscv.h" -#include "field_helpers.h" +#include "dmi.h" -// TODO: DTM_DMI_MAX_ADDRESS_LENGTH should be reduced to 32 (per the debug spec) -#define DTM_DMI_MAX_ADDRESS_LENGTH ((1<<DTM_DTMCS_ABITS_LENGTH)-1) -#define DMI_SCAN_MAX_BIT_LENGTH (DTM_DMI_MAX_ADDRESS_LENGTH + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH) - -#define DMI_SCAN_BUF_SIZE (DIV_ROUND_UP(DMI_SCAN_MAX_BIT_LENGTH, 8)) - -/* Reserve extra room in the batch (needed for the last NOP operation) */ -#define BATCH_RESERVED_SCANS 1 - -static unsigned int get_dmi_scan_length(const struct target *target) -{ - const unsigned int abits = riscv_get_dmi_address_bits(target); - assert(abits > 0); - assert(abits <= DTM_DMI_MAX_ADDRESS_LENGTH); - - return abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH; -} +#include <assert.h> struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans) { - scans += BATCH_RESERVED_SCANS; - struct riscv_batch *out = calloc(1, sizeof(*out)); - if (!out) { - LOG_ERROR("Failed to allocate struct riscv_batch"); - return NULL; - } - - out->target = target; - out->allocated_scans = scans; - out->last_scan = RISCV_SCAN_TYPE_INVALID; - out->was_run = false; - out->last_scan_delay = 0; - - out->data_out = NULL; - out->data_in = NULL; - out->fields = NULL; - out->delay_classes = NULL; - out->bscan_ctxt = NULL; - out->read_keys = NULL; - - /* FIXME: There is potential for memory usage reduction. We could allocate - * smaller buffers than DMI_SCAN_BUF_SIZE (that is, buffers that correspond to - * the real DR scan length on the given target) */ - out->data_out = malloc(sizeof(*out->data_out) * scans * DMI_SCAN_BUF_SIZE); - if (!out->data_out) { - LOG_ERROR("Failed to allocate data_out in RISC-V batch."); - goto alloc_error; - }; - out->data_in = malloc(sizeof(*out->data_in) * scans * DMI_SCAN_BUF_SIZE); - if (!out->data_in) { - LOG_ERROR("Failed to allocate data_in in RISC-V batch."); - goto alloc_error; - } - out->fields = malloc(sizeof(*out->fields) * scans); - if (!out->fields) { - LOG_ERROR("Failed to allocate fields in RISC-V batch."); - goto alloc_error; - } - out->delay_classes = malloc(sizeof(*out->delay_classes) * scans); - if (!out->delay_classes) { - LOG_ERROR("Failed to allocate delay_classes in RISC-V batch."); - goto alloc_error; - } - if (bscan_tunnel_ir_width != 0) { - out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * scans); - if (!out->bscan_ctxt) { - LOG_ERROR("Failed to allocate bscan_ctxt in RISC-V batch."); - goto alloc_error; - } - } - out->read_keys = malloc(sizeof(*out->read_keys) * scans); - if (!out->read_keys) { - LOG_ERROR("Failed to allocate read_keys in RISC-V batch."); - goto alloc_error; - } - - return out; + const struct riscv_dmi_backend_ops *backend = riscv_dmi_backend(target); + assert(backend); + assert(backend->batch_alloc); -alloc_error: - riscv_batch_free(out); - return NULL; + return backend->batch_alloc(target, scans); } void riscv_batch_free(struct riscv_batch *batch) -{ - free(batch->data_in); - free(batch->data_out); - free(batch->fields); - free(batch->delay_classes); - free(batch->bscan_ctxt); - free(batch->read_keys); - free(batch); -} - -bool riscv_batch_full(struct riscv_batch *batch) -{ - return riscv_batch_available_scans(batch) == 0; -} - -static bool riscv_batch_was_scan_busy(const struct riscv_batch *batch, - size_t scan_idx) -{ - assert(batch->was_run); - assert(scan_idx < batch->used_scans); - const struct scan_field *field = batch->fields + scan_idx; - assert(field->in_value); - const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits); - return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY; -} - -static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_idx, - const struct riscv_scan_delays *delays) -{ - if (!batch->was_run) - return; - /* Get the delay type of the scan that resulted in the busy response. - * Since DMI interactions always end with a NOP, if "start_idx" is zero - * the base delay value is used. - */ - const enum riscv_scan_delay_class delay_class = start_idx > 0 - ? batch->delay_classes[start_idx - 1] - : RISCV_DELAY_BASE; - const unsigned int new_delay = riscv_scan_get_delay(delays, delay_class); - if (new_delay <= batch->last_scan_delay) - return; - const unsigned int idle_change = new_delay - batch->last_scan_delay; - LOG_TARGET_DEBUG(batch->target, "Adding %u idle cycles before the batch.", - idle_change); - jtag_add_runtest(idle_change, TAP_IDLE); -} - -static unsigned int get_delay(const struct riscv_batch *batch, size_t scan_idx, - const struct riscv_scan_delays *delays, bool resets_delays, - size_t reset_delays_after) { assert(batch); - assert(scan_idx < batch->used_scans); - const bool delays_were_reset = resets_delays - && (scan_idx >= reset_delays_after); - const enum riscv_scan_delay_class delay_class = - batch->delay_classes[scan_idx]; - const unsigned int delay = riscv_scan_get_delay(delays, delay_class); - return delays_were_reset ? 0 : delay; -} + assert(batch->backend); + assert(batch->backend->batch_free); -static unsigned int decode_dmi(const struct riscv_batch *batch, char *text, - uint32_t address, uint32_t data) -{ - static const struct { - uint32_t address; - enum riscv_debug_reg_ordinal ordinal; - } description[] = { - {DM_DMCONTROL, DM_DMCONTROL_ORDINAL}, - {DM_DMSTATUS, DM_DMSTATUS_ORDINAL}, - {DM_ABSTRACTCS, DM_ABSTRACTCS_ORDINAL}, - {DM_COMMAND, DM_COMMAND_ORDINAL}, - {DM_SBCS, DM_SBCS_ORDINAL} - }; - - for (unsigned int i = 0; i < ARRAY_SIZE(description); i++) { - if (riscv_get_dmi_address(batch->target, description[i].address) - == address) { - const struct riscv_debug_reg_ctx context = { - .XLEN = { .value = 0, .is_set = false }, - .DXLEN = { .value = 0, .is_set = false }, - .abits = { .value = 0, .is_set = false }, - }; - return riscv_debug_reg_to_s(text, description[i].ordinal, - context, data, RISCV_DEBUG_REG_HIDE_ALL_0); - } - } - if (text) - text[0] = '\0'; - return 0; + batch->backend->batch_free(batch); } -static void log_dmi_decoded(const struct riscv_batch *batch, bool write, - uint32_t address, uint32_t data) -{ - const size_t size = decode_dmi(batch, /* text */ NULL, address, data) + 1; - char * const decoded = malloc(size); - if (!decoded) { - LOG_ERROR("Not enough memory to allocate %zu bytes.", size); - return; - } - decode_dmi(batch, decoded, address, data); - LOG_DEBUG("%s: %s", write ? "write" : "read", decoded); - free(decoded); -} - -static void log_batch(const struct riscv_batch *batch, size_t start_idx, - const struct riscv_scan_delays *delays, bool resets_delays, - size_t reset_delays_after) +bool riscv_batch_full(struct riscv_batch *batch) { - if (!LOG_LEVEL_IS(LOG_LVL_DEBUG)) - return; - - const unsigned int abits = riscv_get_dmi_address_bits(batch->target); - - /* Determine the "op" and "address" of the scan that preceded the first - * executed scan. - * FIXME: The code here assumes that there were no DMI operations between - * the last execution of the batch and the current one. - * Caching the info about the last executed DMI scan in "dm013_info_t" - * would be a more robust solution. - */ - bool last_scan_was_read = false; - uint32_t last_scan_address = (uint32_t)(-1) /* to silence maybe-uninitialized */; - if (start_idx > 0) { - const struct scan_field * const field = &batch->fields[start_idx - 1]; - assert(field->out_value); - last_scan_was_read = buf_get_u32(field->out_value, DTM_DMI_OP_OFFSET, - DTM_DMI_OP_LENGTH) == DTM_DMI_OP_READ; - last_scan_address = buf_get_u32(field->out_value, - DTM_DMI_ADDRESS_OFFSET, abits); - } - - /* Decode and log every executed scan */ - for (size_t i = start_idx; i < batch->used_scans; ++i) { - static const char * const op_string[] = {"-", "r", "w", "?"}; - const unsigned int delay = get_delay(batch, i, delays, resets_delays, - reset_delays_after); - const struct scan_field * const field = &batch->fields[i]; - - assert(field->out_value); - const unsigned int out_op = buf_get_u32(field->out_value, - DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); - const uint32_t out_data = buf_get_u32(field->out_value, - DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); - const uint32_t out_address = buf_get_u32(field->out_value, - DTM_DMI_ADDRESS_OFFSET, abits); - if (field->in_value) { - static const char * const status_string[] = { - "+", "?", "F", "b" - }; - const unsigned int in_op = buf_get_u32(field->in_value, - DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); - const uint32_t in_data = buf_get_u32(field->in_value, - DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); - const uint32_t in_address = buf_get_u32(field->in_value, - DTM_DMI_ADDRESS_OFFSET, abits); - - LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 - " -> %s %08" PRIx32 " @%02" PRIx32 "; %ui", - field->num_bits, op_string[out_op], out_data, out_address, - status_string[in_op], in_data, in_address, delay); - - if (last_scan_was_read && in_op == DTM_DMI_OP_SUCCESS) - log_dmi_decoded(batch, /*write*/ false, - last_scan_address, in_data); - } else { - LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 " -> ?; %ui", - field->num_bits, op_string[out_op], out_data, out_address, - delay); - } - - if (out_op == DTM_DMI_OP_WRITE) - log_dmi_decoded(batch, /*write*/ true, out_address, - out_data); - - last_scan_was_read = out_op == DTM_DMI_OP_READ; - last_scan_address = out_address; - } + return riscv_batch_available_scans(batch) == 0; } int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx, const struct riscv_scan_delays *delays, bool resets_delays, size_t reset_delays_after) { - assert(batch->used_scans); - assert(start_idx < batch->used_scans); - assert(batch->last_scan == RISCV_SCAN_TYPE_NOP); - assert(!batch->was_run || riscv_batch_was_scan_busy(batch, start_idx)); - assert(start_idx == 0 || !riscv_batch_was_scan_busy(batch, start_idx - 1)); - - if (batch->was_run) - add_idle_before_batch(batch, start_idx, delays); - - LOG_TARGET_DEBUG(batch->target, "Running batch of scans [%zu, %zu)", - start_idx, batch->used_scans); - - unsigned int delay = 0 /* to silence maybe-uninitialized */; - for (size_t i = start_idx; i < batch->used_scans; ++i) { - if (bscan_tunnel_ir_width != 0) - riscv_add_bscan_tunneled_scan(batch->target->tap, batch->fields + i, - batch->bscan_ctxt + i); - else - jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE); - - delay = get_delay(batch, i, delays, resets_delays, - reset_delays_after); - if (delay > 0) - jtag_add_runtest(delay, TAP_IDLE); - } - - keep_alive(); - - if (jtag_execute_queue() != ERROR_OK) { - LOG_TARGET_ERROR(batch->target, "Unable to execute JTAG queue"); - return ERROR_FAIL; - } - - keep_alive(); + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_run_from); - if (bscan_tunnel_ir_width != 0) { - /* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */ - for (size_t i = start_idx; i < batch->used_scans; ++i) { - if ((batch->fields + i)->in_value) - buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1); - } - } + int result = riscv_dmi_prepare_access(batch->target); + if (result != ERROR_OK) + return result; - log_batch(batch, start_idx, delays, resets_delays, reset_delays_after); - batch->was_run = true; - batch->last_scan_delay = delay; - return ERROR_OK; + return batch->backend->batch_run_from(batch, start_idx, delays, + resets_delays, reset_delays_after); } -void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint32_t address, uint32_t data, - bool read_back, enum riscv_scan_delay_class delay_class) +void riscv_batch_add_dmi_write(struct riscv_batch *batch, uint32_t address, + uint32_t data, bool read_back, + enum riscv_scan_delay_class delay_class) { - // TODO: Check that the bit width of "address" is no more than dtmcs.abits, - // otherwise return an error (during batch creation or when the batch is executed). - - assert(batch->used_scans < batch->allocated_scans); - struct scan_field *field = batch->fields + batch->used_scans; - - field->num_bits = get_dmi_scan_length(batch->target); - assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); - - uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE; - uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE; - - field->out_value = out_value; - riscv_fill_dmi_write(batch->target, out_value, address, data); - - if (read_back) { - field->in_value = in_value; - riscv_fill_dm_nop(batch->target, in_value); - } else { - field->in_value = NULL; - } + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_add_dmi_write); - batch->delay_classes[batch->used_scans] = delay_class; - batch->last_scan = RISCV_SCAN_TYPE_WRITE; - batch->used_scans++; + batch->backend->batch_add_dmi_write(batch, address, data, read_back, + delay_class); } size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, uint32_t address, enum riscv_scan_delay_class delay_class) { - // TODO: Check that the bit width of "address" is no more than dtmcs.abits, - // otherwise return an error (during batch creation or when the batch is executed). - - assert(batch->used_scans < batch->allocated_scans); - struct scan_field *field = batch->fields + batch->used_scans; - - field->num_bits = get_dmi_scan_length(batch->target); - assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); - - uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE; - uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE; - - field->out_value = out_value; - field->in_value = in_value; - riscv_fill_dmi_read(batch->target, out_value, address); - riscv_fill_dm_nop(batch->target, in_value); - - batch->delay_classes[batch->used_scans] = delay_class; - batch->last_scan = RISCV_SCAN_TYPE_READ; - batch->used_scans++; + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_add_dmi_read); - batch->read_keys[batch->read_keys_used] = batch->used_scans; - return batch->read_keys_used++; + return batch->backend->batch_add_dmi_read(batch, address, delay_class); } uint32_t riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key) { - assert(key < batch->read_keys_used); - size_t index = batch->read_keys[key]; - assert(index < batch->used_scans); - uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index; - /* extract "op" field from the DMI read result */ - return buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); -} + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_get_dmi_read_op); -uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key) -{ - assert(key < batch->read_keys_used); - size_t index = batch->read_keys[key]; - assert(index < batch->used_scans); - uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index; - /* extract "data" field from the DMI read result */ - return buf_get_u32(base, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); + return batch->backend->batch_get_dmi_read_op(batch, key); } -void riscv_batch_add_nop(struct riscv_batch *batch) +uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, + size_t key) { - assert(batch->used_scans < batch->allocated_scans); - struct scan_field *field = batch->fields + batch->used_scans; - - field->num_bits = get_dmi_scan_length(batch->target); - assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); - - uint8_t *out_value = batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE; - uint8_t *in_value = batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE; - - field->out_value = out_value; - field->in_value = in_value; - riscv_fill_dm_nop(batch->target, out_value); - riscv_fill_dm_nop(batch->target, in_value); + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_get_dmi_read_data); - /* DMI NOP never triggers any debug module operation, - * so the shortest (base) delay can be used. */ - batch->delay_classes[batch->used_scans] = RISCV_DELAY_BASE; - batch->last_scan = RISCV_SCAN_TYPE_NOP; - batch->used_scans++; + return batch->backend->batch_get_dmi_read_data(batch, key); } size_t riscv_batch_available_scans(struct riscv_batch *batch) { - assert(batch->allocated_scans >= (batch->used_scans + BATCH_RESERVED_SCANS)); - return batch->allocated_scans - batch->used_scans - BATCH_RESERVED_SCANS; + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_available_scans); + + return batch->backend->batch_available_scans(batch); } bool riscv_batch_was_batch_busy(const struct riscv_batch *batch) { - assert(batch->was_run); - assert(batch->used_scans); - assert(batch->last_scan == RISCV_SCAN_TYPE_NOP); - return riscv_batch_was_scan_busy(batch, batch->used_scans - 1); + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_was_busy); + + return batch->backend->batch_was_busy(batch); } size_t riscv_batch_finished_scans(const struct riscv_batch *batch) { - if (!riscv_batch_was_batch_busy(batch)) { - /* Whole batch succeeded. */ - return batch->used_scans; - } - assert(batch->used_scans); - size_t first_busy = 0; - while (!riscv_batch_was_scan_busy(batch, first_busy)) - ++first_busy; - return first_busy; + assert(batch); + assert(batch->backend); + assert(batch->backend->batch_finished_scans); + + return batch->backend->batch_finished_scans(batch); } diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h index 5d8b57234e..881a99b0d6 100644 --- a/src/target/riscv/batch.h +++ b/src/target/riscv/batch.h @@ -3,19 +3,21 @@ #ifndef OPENOCD_TARGET_RISCV_BATCH_H #define OPENOCD_TARGET_RISCV_BATCH_H +#include <assert.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "helper/log.h" #include "target/target.h" -#include "jtag/jtag.h" #include "riscv.h" -enum riscv_scan_type { - RISCV_SCAN_TYPE_INVALID, - RISCV_SCAN_TYPE_NOP, - RISCV_SCAN_TYPE_READ, - RISCV_SCAN_TYPE_WRITE, -}; +struct riscv_dmi_backend_ops; -/* These types are used to specify how many JTAG RTI cycles to add after a - * scan. +/* These types are used to specify backend-specific idle delays after DMI + * accesses. JTAG uses them as RTI cycle classes; direct backends may ignore + * them when the transport has no idle-cycle concept. */ enum riscv_scan_delay_class { /* Delay needed for accessing debug module registers: */ @@ -120,45 +122,13 @@ static inline int riscv_scan_increase_delay(struct riscv_scan_delays *delays, return ERROR_OK; } -/* A batch of multiple JTAG scans, which are grouped together to avoid the - * overhead of some JTAG adapters when sending single commands. This is - * designed to support block copies, as that's what we actually need to go - * fast. */ +/* A batch of multiple DMI operations. Backend-specific batching state is + * private to the selected DTM backend. + */ struct riscv_batch { struct target *target; - - size_t allocated_scans; - size_t used_scans; - - uint8_t *data_out; - uint8_t *data_in; - struct scan_field *fields; - enum riscv_scan_delay_class *delay_classes; - - /* If in BSCAN mode, this field will be allocated (one per scan), - and utilized to tunnel all the scans in the batch. If not in - BSCAN mode, this field is unallocated and stays NULL */ - riscv_bscan_tunneled_scan_context_t *bscan_ctxt; - - /* In JTAG we scan out the previous value's output when performing a - * scan. This is a pain for users, so we just provide them the - * illusion of not having to do this by eliding all but the last NOP. - * */ - enum riscv_scan_type last_scan; - - /* The read keys. */ - size_t *read_keys; - size_t read_keys_used; - - /* Flag indicating that the last run of the batch finished without an error - * from the underlying JTAG layer of OpenOCD - all scans were performed. - * However, RISC-V DMI "busy" condition could still have occurred. - */ - bool was_run; - /* Number of RTI cycles used by the last scan on the last run. - * Only valid when `was_run` is set. - */ - unsigned int last_scan_delay; + const struct riscv_dmi_backend_ops *backend; + void *backend_priv; }; /* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG @@ -169,16 +139,16 @@ void riscv_batch_free(struct riscv_batch *batch); /* Checks to see if this batch is full. */ bool riscv_batch_full(struct riscv_batch *batch); -/* Executes this batch of JTAG DTM DMI scans, starting form "start" scan. +/* Executes this batch of DMI operations, starting from "start" operation. * * If batch is run for the first time, it is expected that "start" is zero. - * It is expected that the batch ends with a DMI NOP operation. * - * "idle_counts" specifies the number of JTAG Run-Test-Idle cycles to add - * after each scan depending on the delay class of the scan. + * "idle_counts" specifies the number of idle cycles to add after each DMI + * operation depending on the delay class of the operation, if the backend + * uses idle cycles. * * If "resets_delays" is true, the algorithm will stop inserting idle cycles - * (JTAG Run-Test-Idle) after "reset_delays_after" number of scans is + * after "reset_delays_after" number of operations is * performed. This is useful for stress-testing of RISC-V algorithms in * OpenOCD that are based on batches. */ @@ -199,7 +169,7 @@ riscv_batch_add_dm_write(struct riscv_batch *batch, uint32_t address, uint32_t d { return riscv_batch_add_dmi_write(batch, riscv_get_dmi_address(batch->target, address), data, - read_back, delay_type); + read_back, delay_type); } /* DM register reads must be handled in two parts: the first one schedules a read and @@ -219,9 +189,6 @@ riscv_batch_add_dm_read(struct riscv_batch *batch, uint32_t address, uint32_t riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key); uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key); -/* Scans in a NOP. */ -void riscv_batch_add_nop(struct riscv_batch *batch); - /* Returns the number of available scans. */ size_t riscv_batch_available_scans(struct riscv_batch *batch); diff --git a/src/target/riscv/dmi.c b/src/target/riscv/dmi.c new file mode 100644 index 0000000000..c561c8b7ae --- /dev/null +++ b/src/target/riscv/dmi.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dmi.h" +#include "dtm.h" +#include "riscv.h" + +#include "helper/log.h" + +#include <assert.h> +#include <string.h> + +const struct riscv_dmi_backend_ops *riscv_dmi_backend(struct target *target) +{ + RISCV_INFO(r); + + return r->dtm ? r->dtm->backend : NULL; +} + +int riscv_dmi_select(struct target *target) +{ + RISCV_INFO(r); + + if (!r->dtm && riscv_dtm_assign_implicit(target) != ERROR_OK) + return ERROR_FAIL; + + if (!r->dtm) + return ERROR_FAIL; + + return riscv_dtm_init(r->dtm); +} + +int riscv_dmi_get_info(struct target *target, struct riscv_dmi_info *info) +{ + assert(info); + memset(info, 0, sizeof(*info)); + + const struct riscv_dmi_backend_ops *backend = riscv_dmi_backend(target); + if (!backend || !backend->get_info) { + LOG_TARGET_ERROR(target, "RISC-V DMI backend is not selected."); + return ERROR_FAIL; + } + + int result = backend->get_info(target, info); + if (result == ERROR_OK) { + RISCV_INFO(r); + riscv_dtm_update_info(r->dtm, info->dtm_version, info->abits, + info->idle, info->has_dtmcs); + } + + return result; +} + +int riscv_dmi_reset(struct target *target) +{ + const struct riscv_dmi_backend_ops *backend = riscv_dmi_backend(target); + if (!backend || !backend->reset) + return ERROR_FAIL; + + return backend->reset(target); +} + +int riscv_dmi_prepare_access(struct target *target) +{ + const struct riscv_dmi_backend_ops *backend = riscv_dmi_backend(target); + if (!backend || !backend->prepare_access) + return ERROR_FAIL; + + return backend->prepare_access(target); +} diff --git a/src/target/riscv/dmi.h b/src/target/riscv/dmi.h new file mode 100644 index 0000000000..6f8b910e1f --- /dev/null +++ b/src/target/riscv/dmi.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_DMI_H +#define OPENOCD_TARGET_RISCV_DMI_H + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#include "batch.h" + +struct target; +struct riscv_dtm; +struct jtag_tap; + +struct riscv_dmi_info { + unsigned int dtm_version; + unsigned int abits; + unsigned int idle; + bool has_dtmcs; +}; + +struct riscv_dmi_backend_ops { + const char *name; + int (*get_info)(struct target *target, struct riscv_dmi_info *info); + int (*reset)(struct target *target); + int (*prepare_access)(struct target *target); + + struct riscv_batch *(*batch_alloc)(struct target *target, size_t scans); + void (*batch_free)(struct riscv_batch *batch); + int (*batch_run_from)(struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after); + void (*batch_add_dmi_write)(struct riscv_batch *batch, uint32_t address, + uint32_t data, bool read_back, enum riscv_scan_delay_class delay_class); + size_t (*batch_add_dmi_read)(struct riscv_batch *batch, uint32_t address, + enum riscv_scan_delay_class delay_class); + uint32_t (*batch_get_dmi_read_op)(const struct riscv_batch *batch, size_t key); + uint32_t (*batch_get_dmi_read_data)(const struct riscv_batch *batch, size_t key); + size_t (*batch_available_scans)(struct riscv_batch *batch); + bool (*batch_was_busy)(const struct riscv_batch *batch); + size_t (*batch_finished_scans)(const struct riscv_batch *batch); +}; + +extern const struct riscv_dmi_backend_ops riscv_dmi_jtag_backend; +extern const struct riscv_dmi_backend_ops riscv_dmi_ap_backend; + +int riscv_dmi_select(struct target *target); +int riscv_dmi_get_info(struct target *target, struct riscv_dmi_info *info); +int riscv_dmi_reset(struct target *target); +int riscv_dmi_prepare_access(struct target *target); +const struct riscv_dmi_backend_ops *riscv_dmi_backend(struct target *target); + +int riscv_dmi_jtag_set_ir(const char *ir_name, uint32_t value); +int riscv_dmi_jtag_use_bscan_tunnel(uint8_t ir_width, int tunnel_type); +int riscv_dmi_jtag_set_bscan_tunnel_ir(int ir_id); +int riscv_dmi_jtag_init_tap(struct jtag_tap *tap); + +#endif /* OPENOCD_TARGET_RISCV_DMI_H */ diff --git a/src/target/riscv/dtm.c b/src/target/riscv/dtm.c new file mode 100644 index 0000000000..da7b231a6e --- /dev/null +++ b/src/target/riscv/dtm.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "dtm.h" +#include "dmi.h" +#include "riscv.h" + +#include "helper/list.h" +#include "helper/log.h" +#include "target/arm_adi_v5.h" +#include "transport/transport.h" + +#include <inttypes.h> +#include <string.h> + +struct riscv_dtm_object { + struct list_head lh; + struct riscv_dtm dtm; + char *name; + bool implicit; +}; + +static OOCD_LIST_HEAD(all_dtm); +static unsigned int implicit_dtm_count; + +static const struct command_registration dtm_instance_commands[]; + +static const struct { + const char *name; + enum riscv_dtm_type type; +} dtm_type_names[] = { + { "jtag", RISCV_DTM_TYPE_JTAG }, + { "ap", RISCV_DTM_TYPE_AP }, +}; + +static const char *riscv_dtm_type_name(enum riscv_dtm_type type) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(dtm_type_names); i++) { + if (dtm_type_names[i].type == type) + return dtm_type_names[i].name; + } + return "unknown"; +} + +static enum riscv_dtm_type riscv_dtm_type_by_name(const char *name) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(dtm_type_names); i++) { + if (!strcmp(dtm_type_names[i].name, name)) + return dtm_type_names[i].type; + } + return RISCV_DTM_TYPE_UNKNOWN; +} + +const char *riscv_dtm_name(const struct riscv_dtm *dtm) +{ + return dtm && dtm->name ? dtm->name : "<unnamed-dtm>"; +} + +struct riscv_dtm *riscv_dtm_by_name(const char *name) +{ + struct riscv_dtm_object *obj; + + list_for_each_entry(obj, &all_dtm, lh) { + if (!strcmp(name, obj->name)) + return &obj->dtm; + } + + return NULL; +} + +struct riscv_dtm *riscv_dtm_by_jim_obj(Jim_Interp *interp, Jim_Obj *obj) +{ + return riscv_dtm_by_name(Jim_GetString(obj, NULL)); +} + +static int riscv_dtm_set_backend(struct riscv_dtm *dtm) +{ + switch (dtm->type) { + case RISCV_DTM_TYPE_JTAG: + dtm->backend = &riscv_dmi_jtag_backend; + return ERROR_OK; + case RISCV_DTM_TYPE_AP: + dtm->backend = &riscv_dmi_ap_backend; + return ERROR_OK; + default: + LOG_ERROR("Unsupported RISC-V DTM type '%s'.", + riscv_dtm_type_name(dtm->type)); + return ERROR_FAIL; + } +} + +int riscv_dtm_init(struct riscv_dtm *dtm) +{ + if (!dtm) + return ERROR_FAIL; + if (dtm->initialized) + return ERROR_OK; + + int result = riscv_dtm_set_backend(dtm); + if (result != ERROR_OK) + return result; + + if (dtm->type == RISCV_DTM_TYPE_JTAG && !dtm->tap) { + LOG_ERROR("RISC-V DTM '%s' has no JTAG TAP.", riscv_dtm_name(dtm)); + return ERROR_FAIL; + } + if (dtm->type == RISCV_DTM_TYPE_JTAG) { + result = riscv_dmi_jtag_init_tap(dtm->tap); + if (result != ERROR_OK) + return result; + } + if (dtm->type == RISCV_DTM_TYPE_AP) { + struct riscv_dmi_ap_config *config = dtm->backend_priv; + if (!config || !config->dap || config->ap_num == DP_APSEL_INVALID) { + LOG_ERROR("RISC-V DTM '%s' needs -dap and -ap-num.", + riscv_dtm_name(dtm)); + return ERROR_FAIL; + } + if (!config->ap) { + config->ap = dap_get_ap(config->dap, config->ap_num); + if (!config->ap) { + LOG_ERROR("Cannot use DAP '%s' AP 0x%08" PRIx64 + " for RISC-V DMI access.", + adiv5_dap_name(config->dap), config->ap_num); + return ERROR_FAIL; + } + } + } + + dtm->initialized = true; + return ERROR_OK; +} + +int riscv_dtm_init_all(void) +{ + struct riscv_dtm_object *obj; + + list_for_each_entry(obj, &all_dtm, lh) { + int result = riscv_dtm_init(&obj->dtm); + if (result != ERROR_OK) + return result; + } + + return ERROR_OK; +} + +int riscv_dtm_assign_target(struct target *target, struct riscv_dtm *dtm) +{ + if (!target || !dtm) + return ERROR_FAIL; + + struct riscv_private_config *config = target->private_config; + if (!config) { + config = calloc(1, sizeof(*config)); + if (!config) + return ERROR_FAIL; + target->private_config = config; + } + + config->dtm = dtm; + target->has_dtm = true; + target->dtm_configured = true; + if (target->arch_info) { + RISCV_INFO(r); + r->dtm = dtm; + } + + if (dtm->type == RISCV_DTM_TYPE_JTAG && dtm->tap) { + target->tap = dtm->tap; + target->tap_configured = true; + } + + return ERROR_OK; +} + +static struct riscv_dtm *riscv_dtm_find_implicit_jtag(struct jtag_tap *tap) +{ + struct riscv_dtm_object *obj; + + list_for_each_entry(obj, &all_dtm, lh) { + if (obj->implicit && obj->dtm.type == RISCV_DTM_TYPE_JTAG + && obj->dtm.tap == tap) + return &obj->dtm; + } + + return NULL; +} + +static struct riscv_dtm *riscv_dtm_find_implicit_ap(struct adiv5_dap *dap, + uint64_t ap_num) +{ + struct riscv_dtm_object *obj; + + list_for_each_entry(obj, &all_dtm, lh) { + struct riscv_dmi_ap_config *config = obj->dtm.backend_priv; + if (obj->implicit && obj->dtm.type == RISCV_DTM_TYPE_AP + && config && config->dap == dap && config->ap_num == ap_num) + return &obj->dtm; + } + + return NULL; +} + +static struct riscv_dtm *riscv_dtm_create_object(const char *name, + enum riscv_dtm_type type, struct jtag_tap *tap, bool implicit) +{ + struct riscv_dtm_object *obj = calloc(1, sizeof(*obj)); + if (!obj) + return NULL; + + obj->name = strdup(name); + if (!obj->name) { + free(obj); + return NULL; + } + + obj->implicit = implicit; + obj->dtm.name = obj->name; + obj->dtm.type = type; + obj->dtm.tap = tap; + obj->dtm.reset_delays_wait = -1; + if (riscv_dtm_set_backend(&obj->dtm) != ERROR_OK) { + free(obj->name); + free(obj); + return NULL; + } + + list_add_tail(&obj->lh, &all_dtm); + return &obj->dtm; +} + +static struct riscv_dtm_object *riscv_dtm_object_by_dtm(struct riscv_dtm *dtm) +{ + return container_of(dtm, struct riscv_dtm_object, dtm); +} + +static void riscv_dtm_destroy_object(struct riscv_dtm_object *obj) +{ + struct riscv_dmi_ap_config *config = obj->dtm.backend_priv; + if (config) { + if (config->ap) + dap_put_ap(config->ap); + free(config); + } + list_del(&obj->lh); + free(obj->name); + free(obj); +} + +int riscv_dtm_configure_ap(struct riscv_dtm *dtm, struct adiv5_dap *dap, + uint64_t ap_num) +{ + if (!dtm || !dap || ap_num == DP_APSEL_INVALID) + return ERROR_FAIL; + + struct riscv_dmi_ap_config *config = dtm->backend_priv; + if (!config) { + config = calloc(1, sizeof(*config)); + if (!config) + return ERROR_FAIL; + config->ap_num = DP_APSEL_INVALID; + dtm->backend_priv = config; + } + + if (config->ap && (config->dap != dap || config->ap_num != ap_num)) { + dap_put_ap(config->ap); + config->ap = NULL; + } + config->dap = dap; + config->ap_num = ap_num; + dtm->initialized = false; + return ERROR_OK; +} + +int riscv_dtm_assign_implicit(struct target *target) +{ + if (!target) + return ERROR_FAIL; + + struct riscv_private_config *config = target->private_config; + if (config && config->dtm) + return riscv_dtm_assign_target(target, config->dtm); + + struct riscv_dtm *dtm = NULL; + enum riscv_dtm_type type = RISCV_DTM_TYPE_UNKNOWN; + if (transport_is_jtag()) { + type = RISCV_DTM_TYPE_JTAG; + dtm = riscv_dtm_find_implicit_jtag(target->tap); + } else { + LOG_TARGET_ERROR(target, "Unsupported RISC-V DTM transport '%s'.", + get_current_transport_name()); + return ERROR_FAIL; + } + + if (!dtm) { + char name[64]; + snprintf(name, sizeof(name), "__implicit_riscv_dtm_%u", + implicit_dtm_count++); + dtm = riscv_dtm_create_object(name, type, target->tap, true); + if (!dtm) + return ERROR_FAIL; + } + + return riscv_dtm_assign_target(target, dtm); +} + +int riscv_dtm_assign_ap_target(struct target *target, struct adiv5_dap *dap, + uint64_t ap_num) +{ + if (!target || !dap || ap_num == DP_APSEL_INVALID) + return ERROR_FAIL; + + struct riscv_dtm *dtm = riscv_dtm_find_implicit_ap(dap, ap_num); + if (!dtm) { + char name[64]; + snprintf(name, sizeof(name), "__implicit_riscv_ap_dtm_%u", + implicit_dtm_count++); + dtm = riscv_dtm_create_object(name, RISCV_DTM_TYPE_AP, NULL, true); + if (!dtm) + return ERROR_FAIL; + if (riscv_dtm_configure_ap(dtm, dap, ap_num) != ERROR_OK) { + riscv_dtm_destroy_object(riscv_dtm_object_by_dtm(dtm)); + return ERROR_FAIL; + } + } + + return riscv_dtm_assign_target(target, dtm); +} + +void riscv_dtm_update_info(struct riscv_dtm *dtm, + unsigned int dtm_version, unsigned int abits, unsigned int idle, + bool has_dtmcs) +{ + if (!dtm) + return; + dtm->dtm_version = dtm_version; + dtm->abits = abits; + dtm->idle = idle; + dtm->has_dtmcs = has_dtmcs; +} + +void riscv_dtm_reset_delays(struct riscv_dtm *dtm, int wait) +{ + if (dtm) + dtm->reset_delays_wait = wait; +} + +void riscv_dtm_cleanup_all(void) +{ + struct riscv_dtm_object *obj, *tmp; + + list_for_each_entry_safe(obj, tmp, &all_dtm, lh) { + riscv_dtm_destroy_object(obj); + } +} + +static int dtm_parse_options(struct command_invocation *cmd, + struct riscv_dtm *dtm, unsigned int start_arg) +{ + for (unsigned int i = start_arg; i < CMD_ARGC; ) { + if (!strcmp(CMD_ARGV[i], "-type")) { + if (++i == CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + enum riscv_dtm_type type = riscv_dtm_type_by_name(CMD_ARGV[i++]); + if (type == RISCV_DTM_TYPE_UNKNOWN) { + command_print(cmd, "Unknown DTM type '%s'", CMD_ARGV[i - 1]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + dtm->type = type; + } else if (!strcmp(CMD_ARGV[i], "-chain-position")) { + if (++i == CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + dtm->tap = jtag_tap_by_string(CMD_ARGV[i]); + if (!dtm->tap) { + command_print(cmd, "Tap '%s' could not be found", CMD_ARGV[i]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + i++; + } else if (!strcmp(CMD_ARGV[i], "-dap")) { + if (++i == CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + struct adiv5_dap *dap = dap_instance_by_jim_obj(cmd->ctx->interp, + CMD_JIMTCL_ARGV[i]); + if (!dap) { + command_print(cmd, "DAP '%s' could not be found", CMD_ARGV[i]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + struct riscv_dmi_ap_config *config = dtm->backend_priv; + if (!config) { + config = calloc(1, sizeof(*config)); + if (!config) + return ERROR_FAIL; + config->ap_num = DP_APSEL_INVALID; + dtm->backend_priv = config; + } + if (config->ap && config->dap != dap) { + dap_put_ap(config->ap); + config->ap = NULL; + } + config->dap = dap; + i++; + } else if (!strcmp(CMD_ARGV[i], "-ap-num")) { + if (++i == CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + jim_wide ap_num; + int e = Jim_GetWide(cmd->ctx->interp, CMD_JIMTCL_ARGV[i], &ap_num); + if (e != JIM_OK) + return ERROR_COMMAND_ARGUMENT_INVALID; + if (ap_num < 0 || (ap_num > DP_APSEL_MAX && (ap_num & 0xfff))) { + command_print(cmd, "Invalid AP number"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + struct riscv_dmi_ap_config *config = dtm->backend_priv; + if (!config) { + config = calloc(1, sizeof(*config)); + if (!config) + return ERROR_FAIL; + dtm->backend_priv = config; + } + if (config->ap && config->ap_num != (uint64_t)ap_num) { + dap_put_ap(config->ap); + config->ap = NULL; + } + config->ap_num = ap_num; + i++; + } else { + command_print(cmd, "Unknown DTM option '%s'", CMD_ARGV[i]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + if (dtm->type == RISCV_DTM_TYPE_JTAG && !dtm->tap) { + command_print(cmd, "-chain-position is required for JTAG DTM"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + if (dtm->type == RISCV_DTM_TYPE_AP) { + struct riscv_dmi_ap_config *config = dtm->backend_priv; + if (!config || !config->dap || config->ap_num == DP_APSEL_INVALID) { + command_print(cmd, "-dap and -ap-num are required for AP DTM"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + return riscv_dtm_set_backend(dtm); +} + +COMMAND_HANDLER(handle_dtm_create) +{ + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (riscv_dtm_by_name(CMD_ARGV[0])) { + command_print(CMD, "DTM '%s' already exists", CMD_ARGV[0]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + if (Jim_GetCommand(CMD_CTX->interp, CMD_JIMTCL_ARGV[0], JIM_NONE)) { + command_print(CMD, "Command '%s' already exists", CMD_ARGV[0]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + struct riscv_dtm *dtm = riscv_dtm_create_object(CMD_ARGV[0], + RISCV_DTM_TYPE_JTAG, NULL, false); + if (!dtm) + return ERROR_FAIL; + + int result = dtm_parse_options(CMD, dtm, 1); + if (result != ERROR_OK) { + riscv_dtm_destroy_object(riscv_dtm_object_by_dtm(dtm)); + return result; + } + + struct riscv_dtm_object *obj = riscv_dtm_object_by_dtm(dtm); + const struct command_registration instance_commands[] = { + { + .name = CMD_ARGV[0], + .mode = COMMAND_ANY, + .usage = "", + .help = "RISC-V DTM instance command group", + .chain = dtm_instance_commands, + }, + COMMAND_REGISTRATION_DONE + }; + + return register_commands_with_data(CMD_CTX, NULL, instance_commands, obj); +} + +COMMAND_HANDLER(handle_dtm_names) +{ + struct riscv_dtm_object *obj; + + list_for_each_entry(obj, &all_dtm, lh) + command_print(CMD, "%s", obj->name); + + return ERROR_OK; +} + +COMMAND_HANDLER(handle_dtm_init) +{ + return riscv_dtm_init_all(); +} + +COMMAND_HANDLER(handle_dtm_info) +{ + struct riscv_dtm *dtm; + if (CMD_DATA) { + if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + dtm = &((struct riscv_dtm_object *)CMD_DATA)->dtm; + } else { + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + dtm = riscv_dtm_by_name(CMD_ARGV[0]); + if (!dtm) { + command_print(CMD, "DTM '%s' not found", CMD_ARGV[0]); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + } + + command_print(CMD, "name %s", riscv_dtm_name(dtm)); + command_print(CMD, "type %s", riscv_dtm_type_name(dtm->type)); + if (dtm->type == RISCV_DTM_TYPE_AP) { + struct riscv_dmi_ap_config *config = dtm->backend_priv; + command_print(CMD, "dap %s", config && config->dap + ? adiv5_dap_name(config->dap) : "<none>"); + command_print(CMD, "ap-num 0x%08" PRIx64, + config ? config->ap_num : (uint64_t)DP_APSEL_INVALID); + } + command_print(CMD, "initialized %u", dtm->initialized ? 1 : 0); + command_print(CMD, "version %u", dtm->dtm_version); + command_print(CMD, "abits %u", dtm->abits); + command_print(CMD, "idle %u", dtm->idle); + return ERROR_OK; +} + +COMMAND_HANDLER(handle_dtm_configure) +{ + if (!CMD_DATA) + return ERROR_FAIL; + + struct riscv_dtm *dtm = &((struct riscv_dtm_object *)CMD_DATA)->dtm; + const bool configure = !strcmp(CMD_NAME, "configure"); + if (!configure) { + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + if (!strcmp(CMD_ARGV[0], "-type")) { + command_print(CMD, "%s", riscv_dtm_type_name(dtm->type)); + } else if (!strcmp(CMD_ARGV[0], "-chain-position") && dtm->tap) { + command_print(CMD, "%s", dtm->tap->dotted_name); + } else if (!strcmp(CMD_ARGV[0], "-dap") && dtm->type == RISCV_DTM_TYPE_AP) { + struct riscv_dmi_ap_config *config = dtm->backend_priv; + if (config && config->dap) + command_print(CMD, "%s", adiv5_dap_name(config->dap)); + } else if (!strcmp(CMD_ARGV[0], "-ap-num") && dtm->type == RISCV_DTM_TYPE_AP) { + struct riscv_dmi_ap_config *config = dtm->backend_priv; + if (config) + command_print(CMD, "0x%08" PRIx64, config->ap_num); + } else { + return ERROR_COMMAND_ARGUMENT_INVALID; + } + return ERROR_OK; + } + + int result = dtm_parse_options(CMD, dtm, 0); + if (result == ERROR_OK) + dtm->initialized = false; + return result; +} + +COMMAND_HANDLER(handle_dtm_reset_delays) +{ + if (!CMD_DATA) + return ERROR_FAIL; + + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + int wait = 0; + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], wait); + + riscv_dtm_reset_delays(&((struct riscv_dtm_object *)CMD_DATA)->dtm, wait); + return ERROR_OK; +} + +static const struct command_registration dtm_instance_commands[] = { + { + .name = "configure", + .handler = handle_dtm_configure, + .mode = COMMAND_ANY, + .usage = "[-type jtag|ap] [-chain-position tap] [-dap dap] [-ap-num num]", + .help = "Configure a RISC-V DTM instance.", + }, + { + .name = "cget", + .handler = handle_dtm_configure, + .mode = COMMAND_ANY, + .usage = "-type|-chain-position|-dap|-ap-num", + .help = "Read a RISC-V DTM instance option.", + }, + { + .name = "info", + .handler = handle_dtm_info, + .mode = COMMAND_ANY, + .usage = "", + .help = "Show RISC-V DTM information.", + }, + { + .name = "reset_delays", + .handler = handle_dtm_reset_delays, + .mode = COMMAND_ANY, + .usage = "[wait]", + .help = "Reset learned RISC-V DMI delays for this DTM.", + }, + COMMAND_REGISTRATION_DONE +}; + +int riscv_dtm_set_ir(const char *ir_name, uint32_t value) +{ + return riscv_dmi_jtag_set_ir(ir_name, value); +} + +int riscv_dtm_use_bscan_tunnel(uint8_t ir_width, int tunnel_type) +{ + return riscv_dmi_jtag_use_bscan_tunnel(ir_width, tunnel_type); +} + +int riscv_dtm_set_bscan_tunnel_ir(int ir_id) +{ + return riscv_dmi_jtag_set_bscan_tunnel_ir(ir_id); +} + +static const struct command_registration dtm_subcommand_handlers[] = { + { + .name = "create", + .handler = handle_dtm_create, + .mode = COMMAND_CONFIG, + .usage = "name [-type jtag|ap] [-chain-position tap] [-dap dap] [-ap-num num]", + .help = "Create a named RISC-V DTM instance.", + }, + { + .name = "names", + .handler = handle_dtm_names, + .mode = COMMAND_ANY, + .usage = "", + .help = "List RISC-V DTM instances.", + }, + { + .name = "init", + .handler = handle_dtm_init, + .mode = COMMAND_ANY, + .usage = "", + .help = "Initialize RISC-V DTM instances.", + }, + { + .name = "info", + .handler = handle_dtm_info, + .mode = COMMAND_ANY, + .usage = "name", + .help = "Show RISC-V DTM information.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration dtm_commands[] = { + { + .name = "dtm", + .mode = COMMAND_ANY, + .usage = "", + .help = "RISC-V DTM command group", + .chain = dtm_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +int riscv_dtm_register_commands(struct command_context *cmd_ctx) +{ + return register_commands(cmd_ctx, NULL, dtm_commands); +} diff --git a/src/target/riscv/dtm.h b/src/target/riscv/dtm.h new file mode 100644 index 0000000000..5f913d00eb --- /dev/null +++ b/src/target/riscv/dtm.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_DTM_H +#define OPENOCD_TARGET_RISCV_DTM_H + +#include <stdint.h> +#include <stdbool.h> + +#include "jtag/jtag.h" +#include "helper/command.h" + +struct target; +struct riscv_dmi_backend_ops; +struct adiv5_ap; +struct adiv5_dap; + +enum riscv_dtm_type { + RISCV_DTM_TYPE_JTAG, + RISCV_DTM_TYPE_AP, + RISCV_DTM_TYPE_UNKNOWN, +}; + +struct riscv_dmi_ap_config { + struct adiv5_dap *dap; + uint64_t ap_num; + struct adiv5_ap *ap; +}; + +struct riscv_dtm { + const char *name; + enum riscv_dtm_type type; + const struct riscv_dmi_backend_ops *backend; + void *backend_priv; + struct jtag_tap *tap; + bool initialized; + + unsigned int dtm_version; + unsigned int abits; + unsigned int idle; + bool has_dtmcs; + int reset_delays_wait; +}; + +const char *riscv_dtm_name(const struct riscv_dtm *dtm); +struct riscv_dtm *riscv_dtm_by_name(const char *name); +struct riscv_dtm *riscv_dtm_by_jim_obj(Jim_Interp *interp, Jim_Obj *obj); +int riscv_dtm_init(struct riscv_dtm *dtm); +int riscv_dtm_init_all(void); +int riscv_dtm_assign_target(struct target *target, struct riscv_dtm *dtm); +int riscv_dtm_assign_ap_target(struct target *target, struct adiv5_dap *dap, + uint64_t ap_num); +int riscv_dtm_assign_implicit(struct target *target); +void riscv_dtm_update_info(struct riscv_dtm *dtm, + unsigned int dtm_version, unsigned int abits, unsigned int idle, + bool has_dtmcs); + +int riscv_dtm_register_commands(struct command_context *cmd_ctx); +void riscv_dtm_cleanup_all(void); + +int riscv_dtm_set_ir(const char *ir_name, uint32_t value); +int riscv_dtm_use_bscan_tunnel(uint8_t ir_width, int tunnel_type); +int riscv_dtm_set_bscan_tunnel_ir(int ir_id); +void riscv_dtm_reset_delays(struct riscv_dtm *dtm, int wait); +int riscv_dtm_configure_ap(struct riscv_dtm *dtm, struct adiv5_dap *dap, + uint64_t ap_num); + +#endif /* OPENOCD_TARGET_RISCV_DTM_H */ diff --git a/src/target/riscv/dtm/adi_ap.c b/src/target/riscv/dtm/adi_ap.c new file mode 100644 index 0000000000..f2f4185c46 --- /dev/null +++ b/src/target/riscv/dtm/adi_ap.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "internal.h" + +#include "target/riscv/debug_defines.h" +#include "target/riscv/riscv.h" + +#include "helper/log.h" +#include "target/arm_adi_v5.h" + +#include <assert.h> +#include <stdlib.h> + +struct riscv_batch *riscv_dmi_batch_alloc_common(struct target *target, + size_t scans, const struct riscv_dmi_backend_ops *backend) +{ + struct riscv_batch *batch = calloc(1, sizeof(*batch)); + if (!batch) + return NULL; + + batch->target = target; + batch->backend = backend; + return batch; +} + +void riscv_dmi_batch_free_common(struct riscv_batch *batch) +{ + free(batch); +} + +struct riscv_batch *riscv_dmi_direct_batch_alloc(struct target *target, + size_t scans, const struct riscv_dmi_backend_ops *backend) +{ + struct riscv_batch *batch = riscv_dmi_batch_alloc_common(target, scans, + backend); + if (!batch) + return NULL; + + struct riscv_dmi_direct_batch *direct = calloc(1, sizeof(*direct)); + if (!direct) { + riscv_dmi_batch_free_common(batch); + return NULL; + } + + direct->allocated_ops = scans; + direct->ops = calloc(scans, sizeof(*direct->ops)); + direct->read_keys = calloc(scans, sizeof(*direct->read_keys)); + if (!direct->ops || !direct->read_keys) { + free(direct->ops); + free(direct->read_keys); + free(direct); + riscv_dmi_batch_free_common(batch); + return NULL; + } + + batch->backend_priv = direct; + return batch; +} + +void riscv_dmi_direct_batch_free(struct riscv_batch *batch) +{ + struct riscv_dmi_direct_batch *direct = batch->backend_priv; + if (direct) { + free(direct->ops); + free(direct->read_keys); + free(direct); + } + riscv_dmi_batch_free_common(batch); +} + +void riscv_dmi_direct_batch_add_write(struct riscv_batch *batch, + uint32_t address, uint32_t data, bool read_back, + enum riscv_scan_delay_class delay_class) +{ + struct riscv_dmi_direct_batch *direct = batch->backend_priv; + assert(direct); + assert(direct->used_ops < direct->allocated_ops); + + struct riscv_dmi_direct_op *op = &direct->ops[direct->used_ops++]; + op->opcode = RV_OP_WRITE; + op->params.write.addr = address; + op->params.write.data_to_target = data; +} + +size_t riscv_dmi_direct_batch_add_read(struct riscv_batch *batch, + uint32_t address, enum riscv_scan_delay_class delay_class) +{ + struct riscv_dmi_direct_batch *direct = batch->backend_priv; + assert(direct); + assert(direct->used_ops < direct->allocated_ops); + + struct riscv_dmi_direct_op *op = &direct->ops[direct->used_ops++]; + op->opcode = RV_OP_READ; + op->params.read.addr = address; + op->params.read.data_from_target = 0; + + direct->read_keys[direct->read_keys_used] = direct->used_ops - 1; + return direct->read_keys_used++; +} + +uint32_t riscv_dmi_direct_batch_get_read_op(const struct riscv_batch *batch, + size_t key) +{ + return DTM_DMI_OP_SUCCESS; +} + +uint32_t riscv_dmi_direct_batch_get_read_data(const struct riscv_batch *batch, + size_t key) +{ + const struct riscv_dmi_direct_batch *direct = batch->backend_priv; + assert(direct); + assert(key < direct->read_keys_used); + size_t index = direct->read_keys[key]; + assert(index < direct->used_ops); + + return direct->ops[index].params.read.data_from_target; +} + +size_t riscv_dmi_direct_batch_available_scans(struct riscv_batch *batch) +{ + const struct riscv_dmi_direct_batch *direct = batch->backend_priv; + assert(direct); + + if (direct->used_ops >= direct->allocated_ops) + return 0; + return direct->allocated_ops - direct->used_ops; +} + +bool riscv_dmi_direct_batch_was_busy(const struct riscv_batch *batch) +{ + return false; +} + +size_t riscv_dmi_direct_batch_finished_scans(const struct riscv_batch *batch) +{ + const struct riscv_dmi_direct_batch *direct = batch->backend_priv; + assert(direct); + + return direct->used_ops; +} + +static struct riscv_dmi_ap_config *riscv_dmi_ap_config(struct target *target) +{ + RISCV_INFO(r); + + if (!r->dtm) + return NULL; + return r->dtm->backend_priv; +} + +static int riscv_dmi_get_info_ap(struct target *target, + struct riscv_dmi_info *info) +{ + info->dtm_version = DTM_DTMCS_VERSION_1_0; + info->abits = RISCV013_DTMCS_ABITS_MAX; + info->idle = 0; + info->has_dtmcs = false; + return ERROR_OK; +} + +static int riscv_dmi_reset_ap(struct target *target) +{ + return ERROR_OK; +} + +static int riscv_dmi_prepare_access_ap(struct target *target) +{ + struct riscv_dmi_ap_config *config = riscv_dmi_ap_config(target); + if (!config || !config->ap) { + LOG_TARGET_ERROR(target, "RISC-V AP-backed DTM is not initialized."); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static struct riscv_batch *riscv_batch_alloc_ap(struct target *target, + size_t scans) +{ + return riscv_dmi_direct_batch_alloc(target, scans, &riscv_dmi_ap_backend); +} + +static int riscv_batch_run_from_ap(struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after) +{ + assert(start_idx == 0); + + struct riscv_dmi_ap_config *config = riscv_dmi_ap_config(batch->target); + if (!config || !config->ap) + return ERROR_FAIL; + + struct riscv_dmi_direct_batch *direct = batch->backend_priv; + assert(direct); + + keep_alive(); + for (size_t i = 0; i < direct->used_ops; ++i) { + int result = ERROR_OK; + struct riscv_dmi_direct_op *op = &direct->ops[i]; + + if (op->opcode == RV_OP_READ) { + result = mem_ap_read_u32(config->ap, op->params.read.addr * 4, + &op->params.read.data_from_target); + } + if (op->opcode == RV_OP_WRITE) { + result = mem_ap_write_u32(config->ap, op->params.write.addr * 4, + op->params.write.data_to_target); + } + if (result != ERROR_OK) + return result; + } + + int result = dap_run(config->ap->dap); + if (result != ERROR_OK) + return result; + + keep_alive(); + return ERROR_OK; +} + +const struct riscv_dmi_backend_ops riscv_dmi_ap_backend = { + .name = "ap", + .get_info = riscv_dmi_get_info_ap, + .reset = riscv_dmi_reset_ap, + .prepare_access = riscv_dmi_prepare_access_ap, + .batch_alloc = riscv_batch_alloc_ap, + .batch_free = riscv_dmi_direct_batch_free, + .batch_run_from = riscv_batch_run_from_ap, + .batch_add_dmi_write = riscv_dmi_direct_batch_add_write, + .batch_add_dmi_read = riscv_dmi_direct_batch_add_read, + .batch_get_dmi_read_op = riscv_dmi_direct_batch_get_read_op, + .batch_get_dmi_read_data = riscv_dmi_direct_batch_get_read_data, + .batch_available_scans = riscv_dmi_direct_batch_available_scans, + .batch_was_busy = riscv_dmi_direct_batch_was_busy, + .batch_finished_scans = riscv_dmi_direct_batch_finished_scans, +}; diff --git a/src/target/riscv/dtm/internal.h b/src/target/riscv/dtm/internal.h new file mode 100644 index 0000000000..389578248d --- /dev/null +++ b/src/target/riscv/dtm/internal.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_TARGET_RISCV_DTM_INTERNAL_H +#define OPENOCD_TARGET_RISCV_DTM_INTERNAL_H + +#include "target/riscv/batch.h" +#include "target/riscv/dmi.h" +#include "target/riscv/dtm.h" + +enum riscv_dmi_direct_opcode { + RV_OP_INVALID = 0, + RV_OP_READ, + RV_OP_WRITE, +}; + +struct riscv_dmi_direct_op { + enum riscv_dmi_direct_opcode opcode; + union { + struct { + unsigned int addr; + uint32_t data_from_target; + } read; + struct { + unsigned int addr; + uint32_t data_to_target; + } write; + } params; +}; + +struct riscv_dmi_direct_batch { + size_t allocated_ops; + size_t used_ops; + struct riscv_dmi_direct_op *ops; + size_t *read_keys; + size_t read_keys_used; +}; + +struct riscv_batch *riscv_dmi_batch_alloc_common(struct target *target, + size_t scans, const struct riscv_dmi_backend_ops *backend); +void riscv_dmi_batch_free_common(struct riscv_batch *batch); + +struct riscv_batch *riscv_dmi_direct_batch_alloc(struct target *target, + size_t scans, const struct riscv_dmi_backend_ops *backend); +void riscv_dmi_direct_batch_free(struct riscv_batch *batch); +void riscv_dmi_direct_batch_add_write(struct riscv_batch *batch, + uint32_t address, uint32_t data, bool read_back, + enum riscv_scan_delay_class delay_class); +size_t riscv_dmi_direct_batch_add_read(struct riscv_batch *batch, + uint32_t address, enum riscv_scan_delay_class delay_class); +uint32_t riscv_dmi_direct_batch_get_read_op(const struct riscv_batch *batch, + size_t key); +uint32_t riscv_dmi_direct_batch_get_read_data(const struct riscv_batch *batch, + size_t key); +size_t riscv_dmi_direct_batch_available_scans(struct riscv_batch *batch); +bool riscv_dmi_direct_batch_was_busy(const struct riscv_batch *batch); +size_t riscv_dmi_direct_batch_finished_scans(const struct riscv_batch *batch); + +#endif /* OPENOCD_TARGET_RISCV_DTM_INTERNAL_H */ diff --git a/src/target/riscv/dtm/jtag.c b/src/target/riscv/dtm/jtag.c new file mode 100644 index 0000000000..605b7c9a36 --- /dev/null +++ b/src/target/riscv/dtm/jtag.c @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "internal.h" + +#include "target/riscv/debug_defines.h" +#include "target/riscv/debug_reg_printer.h" +#include "target/riscv/field_helpers.h" +#include "target/riscv/riscv.h" + +#include "helper/binarybuffer.h" +#include "helper/log.h" +#include "jtag/jtag.h" + +#include <assert.h> +#include <inttypes.h> +#include <stdlib.h> + +#define DTM_DMI_MAX_ADDRESS_LENGTH ((1 << DTM_DTMCS_ABITS_LENGTH) - 1) +#define DMI_SCAN_MAX_BIT_LENGTH \ + (DTM_DMI_MAX_ADDRESS_LENGTH + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH) +#define DMI_SCAN_BUF_SIZE (DIV_ROUND_UP(DMI_SCAN_MAX_BIT_LENGTH, 8)) + +/* Reserve extra room in the batch for the final DMI NOP. */ +#define BATCH_RESERVED_SCANS 1 + +enum riscv_scan_type { + RISCV_SCAN_TYPE_INVALID, + RISCV_SCAN_TYPE_NOP, + RISCV_SCAN_TYPE_READ, + RISCV_SCAN_TYPE_WRITE, +}; + +struct riscv_dmi_jtag_batch { + size_t allocated_scans; + size_t used_scans; + + uint8_t *data_out; + uint8_t *data_in; + struct scan_field *fields; + enum riscv_scan_delay_class *delay_classes; + + riscv_bscan_tunneled_scan_context_t *bscan_ctxt; + + enum riscv_scan_type last_scan; + size_t *read_keys; + size_t read_keys_used; + + bool was_run; + unsigned int last_scan_delay; + bool finalized; +}; + +static struct riscv_dmi_jtag_batch *riscv_jtag_batch(struct riscv_batch *batch) +{ + return batch->backend_priv; +} + +static const struct riscv_dmi_jtag_batch *riscv_jtag_batch_const(const struct riscv_batch *batch) +{ + return batch->backend_priv; +} + +static unsigned int get_dmi_scan_length(const struct target *target) +{ + const unsigned int abits = riscv_get_dmi_address_bits(target); + assert(abits > 0); + assert(abits <= DTM_DMI_MAX_ADDRESS_LENGTH); + + return abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH; +} + +static int riscv_dmi_get_info_jtag(struct target *target, + struct riscv_dmi_info *info) +{ + uint32_t dtmcontrol; + if (dtmcs_scan(target->tap, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { + LOG_TARGET_ERROR(target, + "Could not read dtmcontrol. Check JTAG connectivity/board power."); + return ERROR_FAIL; + } + + LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol); + info->dtm_version = get_field(dtmcontrol, DTM_DTMCS_VERSION); + info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS); + info->idle = get_field(dtmcontrol, DTM_DTMCS_IDLE); + info->has_dtmcs = true; + return ERROR_OK; +} + +static int riscv_dmi_reset_jtag(struct target *target) +{ + return dtmcs_scan(target->tap, DTM_DTMCS_DMIRESET, NULL); +} + +static int riscv_dmi_prepare_access_jtag(struct target *target) +{ + struct jtag_tap *tap = target->tap; + + if (bscan_tunnel_ir_width != 0) { + select_dmi_via_bscan(tap); + return ERROR_OK; + } + if (!tap->enabled) + LOG_ERROR("BUG: Target's TAP '%s' is disabled!", jtag_tap_name(tap)); + + bool need_ir_scan = false; + for (struct jtag_tap *other_tap = jtag_tap_next_enabled(NULL); + other_tap; other_tap = jtag_tap_next_enabled(other_tap)) { + if (other_tap != tap) { + if (!other_tap->bypass) { + need_ir_scan = true; + break; + } + } else if (!buf_eq(tap->cur_instr, select_dbus.out_value, tap->ir_length)) { + need_ir_scan = true; + break; + } + } + + if (need_ir_scan) + jtag_add_ir_scan(tap, &select_dbus, TAP_IDLE); + + return ERROR_OK; +} + +static void riscv_batch_add_nop_jtag(struct riscv_batch *batch); + +static struct riscv_batch *riscv_batch_alloc_jtag(struct target *target, + size_t scans) +{ + scans += BATCH_RESERVED_SCANS; + struct riscv_batch *out = riscv_dmi_batch_alloc_common(target, scans, + &riscv_dmi_jtag_backend); + if (!out) { + LOG_ERROR("Failed to allocate struct riscv_batch"); + return NULL; + } + + struct riscv_dmi_jtag_batch *jtag = calloc(1, sizeof(*jtag)); + if (!jtag) { + LOG_ERROR("Failed to allocate RISC-V JTAG batch state."); + riscv_dmi_batch_free_common(out); + return NULL; + } + + out->backend_priv = jtag; + jtag->allocated_scans = scans; + jtag->last_scan = RISCV_SCAN_TYPE_INVALID; + jtag->was_run = false; + jtag->last_scan_delay = 0; + + /* FIXME: Allocate only enough buffer space for the target's real DMI + * scan length instead of the architectural maximum. + */ + jtag->data_out = malloc(sizeof(*jtag->data_out) * scans * DMI_SCAN_BUF_SIZE); + if (!jtag->data_out) { + LOG_ERROR("Failed to allocate data_out in RISC-V batch."); + goto alloc_error; + } + jtag->data_in = malloc(sizeof(*jtag->data_in) * scans * DMI_SCAN_BUF_SIZE); + if (!jtag->data_in) { + LOG_ERROR("Failed to allocate data_in in RISC-V batch."); + goto alloc_error; + } + jtag->fields = malloc(sizeof(*jtag->fields) * scans); + if (!jtag->fields) { + LOG_ERROR("Failed to allocate fields in RISC-V batch."); + goto alloc_error; + } + jtag->delay_classes = malloc(sizeof(*jtag->delay_classes) * scans); + if (!jtag->delay_classes) { + LOG_ERROR("Failed to allocate delay_classes in RISC-V batch."); + goto alloc_error; + } + if (bscan_tunnel_ir_width != 0) { + jtag->bscan_ctxt = malloc(sizeof(*jtag->bscan_ctxt) * scans); + if (!jtag->bscan_ctxt) { + LOG_ERROR("Failed to allocate bscan_ctxt in RISC-V batch."); + goto alloc_error; + } + } + jtag->read_keys = malloc(sizeof(*jtag->read_keys) * scans); + if (!jtag->read_keys) { + LOG_ERROR("Failed to allocate read_keys in RISC-V batch."); + goto alloc_error; + } + + return out; + +alloc_error: + riscv_dmi_jtag_backend.batch_free(out); + return NULL; +} + +static void riscv_batch_free_jtag(struct riscv_batch *batch) +{ + struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch(batch); + if (jtag) { + free(jtag->data_in); + free(jtag->data_out); + free(jtag->fields); + free(jtag->delay_classes); + free(jtag->bscan_ctxt); + free(jtag->read_keys); + free(jtag); + } + riscv_dmi_batch_free_common(batch); +} + +static bool riscv_batch_was_scan_busy(const struct riscv_batch *batch, + size_t scan_idx) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + assert(jtag); + assert(jtag->was_run); + assert(scan_idx < jtag->used_scans); + const struct scan_field *field = jtag->fields + scan_idx; + assert(field->in_value); + const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits); + return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY; +} + +static void add_idle_before_batch(const struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + if (!jtag->was_run) + return; + + const enum riscv_scan_delay_class delay_class = start_idx > 0 + ? jtag->delay_classes[start_idx - 1] + : RISCV_DELAY_BASE; + const unsigned int new_delay = riscv_scan_get_delay(delays, delay_class); + if (new_delay <= jtag->last_scan_delay) + return; + const unsigned int idle_change = new_delay - jtag->last_scan_delay; + LOG_TARGET_DEBUG(batch->target, "Adding %u idle cycles before the batch.", + idle_change); + jtag_add_runtest(idle_change, TAP_IDLE); +} + +static unsigned int get_delay(const struct riscv_batch *batch, size_t scan_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + assert(jtag); + assert(scan_idx < jtag->used_scans); + const bool delays_were_reset = resets_delays + && (scan_idx >= reset_delays_after); + const enum riscv_scan_delay_class delay_class = + jtag->delay_classes[scan_idx]; + const unsigned int delay = riscv_scan_get_delay(delays, delay_class); + return delays_were_reset ? 0 : delay; +} + +static unsigned int decode_dmi(const struct riscv_batch *batch, char *text, + uint32_t address, uint32_t data) +{ + static const struct { + uint32_t address; + enum riscv_debug_reg_ordinal ordinal; + } description[] = { + {DM_DMCONTROL, DM_DMCONTROL_ORDINAL}, + {DM_DMSTATUS, DM_DMSTATUS_ORDINAL}, + {DM_ABSTRACTCS, DM_ABSTRACTCS_ORDINAL}, + {DM_COMMAND, DM_COMMAND_ORDINAL}, + {DM_SBCS, DM_SBCS_ORDINAL} + }; + + for (unsigned int i = 0; i < ARRAY_SIZE(description); i++) { + if (riscv_get_dmi_address(batch->target, description[i].address) + == address) { + const struct riscv_debug_reg_ctx context = { + .XLEN = { .value = 0, .is_set = false }, + .DXLEN = { .value = 0, .is_set = false }, + .abits = { .value = 0, .is_set = false }, + }; + return riscv_debug_reg_to_s(text, description[i].ordinal, + context, data, RISCV_DEBUG_REG_HIDE_ALL_0); + } + } + if (text) + text[0] = '\0'; + return 0; +} + +static void log_dmi_decoded(const struct riscv_batch *batch, bool write, + uint32_t address, uint32_t data) +{ + const size_t size = decode_dmi(batch, NULL, address, data) + 1; + char * const decoded = malloc(size); + if (!decoded) { + LOG_ERROR("Not enough memory to allocate %zu bytes.", size); + return; + } + decode_dmi(batch, decoded, address, data); + LOG_DEBUG("%s: %s", write ? "write" : "read", decoded); + free(decoded); +} + +static void log_batch(const struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after) +{ + if (!LOG_LEVEL_IS(LOG_LVL_DEBUG)) + return; + + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + const unsigned int abits = riscv_get_dmi_address_bits(batch->target); + + bool last_scan_was_read = false; + uint32_t last_scan_address = (uint32_t)(-1); + if (start_idx > 0) { + const struct scan_field * const field = &jtag->fields[start_idx - 1]; + assert(field->out_value); + last_scan_was_read = buf_get_u32(field->out_value, DTM_DMI_OP_OFFSET, + DTM_DMI_OP_LENGTH) == DTM_DMI_OP_READ; + last_scan_address = buf_get_u32(field->out_value, + DTM_DMI_ADDRESS_OFFSET, abits); + } + + for (size_t i = start_idx; i < jtag->used_scans; ++i) { + static const char * const op_string[] = {"-", "r", "w", "?"}; + const unsigned int delay = get_delay(batch, i, delays, resets_delays, + reset_delays_after); + const struct scan_field * const field = &jtag->fields[i]; + + assert(field->out_value); + const unsigned int out_op = buf_get_u32(field->out_value, + DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); + const uint32_t out_data = buf_get_u32(field->out_value, + DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); + const uint32_t out_address = buf_get_u32(field->out_value, + DTM_DMI_ADDRESS_OFFSET, abits); + if (field->in_value) { + static const char * const status_string[] = { + "+", "?", "F", "b" + }; + const unsigned int in_op = buf_get_u32(field->in_value, + DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); + const uint32_t in_data = buf_get_u32(field->in_value, + DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); + const uint32_t in_address = buf_get_u32(field->in_value, + DTM_DMI_ADDRESS_OFFSET, abits); + + LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 + " -> %s %08" PRIx32 " @%02" PRIx32 "; %ui", + field->num_bits, op_string[out_op], out_data, out_address, + status_string[in_op], in_data, in_address, delay); + + if (last_scan_was_read && in_op == DTM_DMI_OP_SUCCESS) + log_dmi_decoded(batch, false, last_scan_address, in_data); + } else { + LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 " -> ?; %ui", + field->num_bits, op_string[out_op], out_data, out_address, + delay); + } + + if (out_op == DTM_DMI_OP_WRITE) + log_dmi_decoded(batch, true, out_address, out_data); + + last_scan_was_read = out_op == DTM_DMI_OP_READ; + last_scan_address = out_address; + } +} + +static int riscv_batch_run_from_jtag(struct riscv_batch *batch, size_t start_idx, + const struct riscv_scan_delays *delays, bool resets_delays, + size_t reset_delays_after) +{ + struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch(batch); + if (!jtag->finalized) + riscv_batch_add_nop_jtag(batch); + + assert(jtag->used_scans); + assert(start_idx < jtag->used_scans); + assert(jtag->last_scan == RISCV_SCAN_TYPE_NOP); + assert(!jtag->was_run || riscv_batch_was_scan_busy(batch, start_idx)); + assert(start_idx == 0 || !riscv_batch_was_scan_busy(batch, start_idx - 1)); + + if (jtag->was_run) + add_idle_before_batch(batch, start_idx, delays); + + LOG_TARGET_DEBUG(batch->target, "Running batch of scans [%zu, %zu)", + start_idx, jtag->used_scans); + + unsigned int delay = 0; + for (size_t i = start_idx; i < jtag->used_scans; ++i) { + if (bscan_tunnel_ir_width != 0) + riscv_add_bscan_tunneled_scan(batch->target->tap, + jtag->fields + i, jtag->bscan_ctxt + i); + else + jtag_add_dr_scan(batch->target->tap, 1, jtag->fields + i, + TAP_IDLE); + + delay = get_delay(batch, i, delays, resets_delays, + reset_delays_after); + if (delay > 0) + jtag_add_runtest(delay, TAP_IDLE); + } + + keep_alive(); + + if (jtag_execute_queue() != ERROR_OK) { + LOG_TARGET_ERROR(batch->target, "Unable to execute JTAG queue"); + return ERROR_FAIL; + } + + keep_alive(); + + if (bscan_tunnel_ir_width != 0) { + for (size_t i = start_idx; i < jtag->used_scans; ++i) { + if ((jtag->fields + i)->in_value) + buffer_shr((jtag->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1); + } + } + + log_batch(batch, start_idx, delays, resets_delays, reset_delays_after); + jtag->was_run = true; + jtag->last_scan_delay = delay; + return ERROR_OK; +} + +static void riscv_batch_add_dmi_write_jtag(struct riscv_batch *batch, + uint32_t address, uint32_t data, bool read_back, + enum riscv_scan_delay_class delay_class) +{ + struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch(batch); + assert(jtag->used_scans < jtag->allocated_scans); + struct scan_field *field = jtag->fields + jtag->used_scans; + + field->num_bits = get_dmi_scan_length(batch->target); + assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); + + uint8_t *out_value = jtag->data_out + jtag->used_scans * DMI_SCAN_BUF_SIZE; + uint8_t *in_value = jtag->data_in + jtag->used_scans * DMI_SCAN_BUF_SIZE; + + field->out_value = out_value; + riscv_fill_dmi_write(batch->target, out_value, address, data); + + if (read_back) { + field->in_value = in_value; + riscv_fill_dm_nop(batch->target, in_value); + } else { + field->in_value = NULL; + } + + jtag->delay_classes[jtag->used_scans] = delay_class; + jtag->last_scan = RISCV_SCAN_TYPE_WRITE; + jtag->used_scans++; +} + +static size_t riscv_batch_add_dmi_read_jtag(struct riscv_batch *batch, + uint32_t address, enum riscv_scan_delay_class delay_class) +{ + struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch(batch); + assert(jtag->used_scans < jtag->allocated_scans); + struct scan_field *field = jtag->fields + jtag->used_scans; + + field->num_bits = get_dmi_scan_length(batch->target); + assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); + + uint8_t *out_value = jtag->data_out + jtag->used_scans * DMI_SCAN_BUF_SIZE; + uint8_t *in_value = jtag->data_in + jtag->used_scans * DMI_SCAN_BUF_SIZE; + + field->out_value = out_value; + field->in_value = in_value; + riscv_fill_dmi_read(batch->target, out_value, address); + riscv_fill_dm_nop(batch->target, in_value); + + jtag->delay_classes[jtag->used_scans] = delay_class; + jtag->last_scan = RISCV_SCAN_TYPE_READ; + jtag->used_scans++; + + jtag->read_keys[jtag->read_keys_used] = jtag->used_scans; + return jtag->read_keys_used++; +} + +static uint32_t riscv_batch_get_dmi_read_op_jtag(const struct riscv_batch *batch, size_t key) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + assert(key < jtag->read_keys_used); + size_t index = jtag->read_keys[key]; + assert(index < jtag->used_scans); + uint8_t *base = jtag->data_in + DMI_SCAN_BUF_SIZE * index; + return buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH); +} + +static uint32_t riscv_batch_get_dmi_read_data_jtag(const struct riscv_batch *batch, size_t key) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + assert(key < jtag->read_keys_used); + size_t index = jtag->read_keys[key]; + assert(index < jtag->used_scans); + uint8_t *base = jtag->data_in + DMI_SCAN_BUF_SIZE * index; + return buf_get_u32(base, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH); +} + +static void riscv_batch_add_nop_jtag(struct riscv_batch *batch) +{ + struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch(batch); + if (jtag->finalized) + return; + + assert(jtag->used_scans < jtag->allocated_scans); + struct scan_field *field = jtag->fields + jtag->used_scans; + + field->num_bits = get_dmi_scan_length(batch->target); + assert(field->num_bits <= DMI_SCAN_MAX_BIT_LENGTH); + + uint8_t *out_value = jtag->data_out + jtag->used_scans * DMI_SCAN_BUF_SIZE; + uint8_t *in_value = jtag->data_in + jtag->used_scans * DMI_SCAN_BUF_SIZE; + + field->out_value = out_value; + field->in_value = in_value; + riscv_fill_dm_nop(batch->target, out_value); + riscv_fill_dm_nop(batch->target, in_value); + + jtag->delay_classes[jtag->used_scans] = RISCV_DELAY_BASE; + jtag->last_scan = RISCV_SCAN_TYPE_NOP; + jtag->used_scans++; + jtag->finalized = true; +} + +static size_t riscv_batch_available_scans_jtag(struct riscv_batch *batch) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + assert(jtag->allocated_scans >= (jtag->used_scans + BATCH_RESERVED_SCANS)); + return jtag->allocated_scans - jtag->used_scans - BATCH_RESERVED_SCANS; +} + +static bool riscv_batch_was_batch_busy_jtag(const struct riscv_batch *batch) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + assert(jtag->was_run); + assert(jtag->used_scans); + assert(jtag->last_scan == RISCV_SCAN_TYPE_NOP); + return riscv_batch_was_scan_busy(batch, jtag->used_scans - 1); +} + +static size_t riscv_batch_finished_scans_jtag(const struct riscv_batch *batch) +{ + const struct riscv_dmi_jtag_batch *jtag = riscv_jtag_batch_const(batch); + if (!riscv_batch_was_batch_busy(batch)) + return jtag->used_scans; + + assert(jtag->used_scans); + size_t first_busy = 0; + while (!riscv_batch_was_scan_busy(batch, first_busy)) + ++first_busy; + return first_busy; +} + +const struct riscv_dmi_backend_ops riscv_dmi_jtag_backend = { + .name = "jtag", + .get_info = riscv_dmi_get_info_jtag, + .reset = riscv_dmi_reset_jtag, + .prepare_access = riscv_dmi_prepare_access_jtag, + .batch_alloc = riscv_batch_alloc_jtag, + .batch_free = riscv_batch_free_jtag, + .batch_run_from = riscv_batch_run_from_jtag, + .batch_add_dmi_write = riscv_batch_add_dmi_write_jtag, + .batch_add_dmi_read = riscv_batch_add_dmi_read_jtag, + .batch_get_dmi_read_op = riscv_batch_get_dmi_read_op_jtag, + .batch_get_dmi_read_data = riscv_batch_get_dmi_read_data_jtag, + .batch_available_scans = riscv_batch_available_scans_jtag, + .batch_was_busy = riscv_batch_was_batch_busy_jtag, + .batch_finished_scans = riscv_batch_finished_scans_jtag, +}; diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c index b4664d2a7f..725cd5d7f8 100644 --- a/src/target/riscv/riscv-013.c +++ b/src/target/riscv/riscv-013.c @@ -32,6 +32,7 @@ #include "program.h" #include "batch.h" #include "debug_reg_printer.h" +#include "dmi.h" #include "field_helpers.h" static int riscv013_on_step_or_resume(struct target *target, bool step); @@ -403,46 +404,11 @@ static uint32_t set_dmcontrol_hartsel(uint32_t initial, int hart_index) return initial; } -/*** Utility functions. ***/ - -static void select_dmi(struct jtag_tap *tap) -{ - if (bscan_tunnel_ir_width != 0) { - select_dmi_via_bscan(tap); - return; - } - if (!tap->enabled) - LOG_ERROR("BUG: Target's TAP '%s' is disabled!", jtag_tap_name(tap)); - - bool need_ir_scan = false; - /* FIXME: make "tap" a const pointer. */ - for (struct jtag_tap *other_tap = jtag_tap_next_enabled(NULL); - other_tap; other_tap = jtag_tap_next_enabled(other_tap)) { - if (other_tap != tap) { - /* Different TAP than ours - check if it is in bypass */ - if (!other_tap->bypass) { - need_ir_scan = true; - break; - } - } else { - /* Our TAP - check if the correct instruction is already loaded */ - if (!buf_eq(tap->cur_instr, select_dbus.out_value, tap->ir_length)) { - need_ir_scan = true; - break; - } - } - } - - if (need_ir_scan) - jtag_add_ir_scan(tap, &select_dbus, TAP_IDLE); -} - static int increase_dmi_busy_delay(struct target *target) { RISCV013_INFO(info); - int res = dtmcs_scan(target->tap, DTM_DTMCS_DMIRESET, - NULL /* discard result */); + int res = riscv_dmi_reset(target); if (res != ERROR_OK) return res; @@ -2026,34 +1992,21 @@ static int examine_dm(struct target *target) static int examine(struct target *target) { + riscv013_info_t *info = get_info(target); /* We reset target state in case if something goes wrong during examine: * DTM/DM scans could fail or hart may fail to halt. */ target->state = TARGET_UNKNOWN; target->debug_reason = DBG_REASON_UNDEFINED; - /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ + info->index = target->coreid; LOG_TARGET_DEBUG(target, "dbgbase=0x%x", target->dbgbase); - uint32_t dtmcontrol; - if (dtmcs_scan(target->tap, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { - LOG_TARGET_ERROR(target, "Could not scan dtmcontrol. Check JTAG connectivity/board power."); + struct riscv_dmi_info dmi_info; + if (riscv_dmi_get_info(target, &dmi_info) != ERROR_OK) return ERROR_FAIL; - } - LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol); - LOG_DEBUG_REG(target, DTM_DTMCS, dtmcontrol); - - if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) { - LOG_TARGET_ERROR(target, "Unsupported DTM version %" PRIu32 ". (dtmcontrol=0x%" PRIx32 ")", - get_field32(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol); - return ERROR_FAIL; - } - - riscv013_info_t *info = get_info(target); - - info->index = target->coreid; - info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS); - info->dtmcs_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE); + info->abits = dmi_info.abits; + info->dtmcs_idle = dmi_info.idle; if (info->abits > RISCV013_DTMCS_ABITS_MAX) { /* Max. address width given by the debug specification is exceeded */ @@ -2066,7 +2019,7 @@ static int examine(struct target *target) if (info->abits == 0) { LOG_TARGET_ERROR(target, - "dtmcs.abits is zero. Check JTAG connectivity/board power"); + "The target's debug bus (DMI) address width is zero."); return ERROR_FAIL; } if (info->abits < RISCV013_DTMCS_ABITS_MIN) { @@ -2559,17 +2512,12 @@ static int batch_run(struct target *target, struct riscv_batch *batch) { RISCV_INFO(r); RISCV013_INFO(info); - select_dmi(target->tap); - riscv_batch_add_nop(batch); - const int result = riscv_batch_run_from(batch, 0, &info->learned_delays, + int result = riscv_batch_run_from(batch, 0, &info->learned_delays, /*resets_delays*/ r->reset_delays_wait >= 0, r->reset_delays_wait); if (result != ERROR_OK) return result; - /* TODO: To use `riscv_batch_finished_scans()` here, it is needed for - * all scans to not discard input, meaning - * "riscv_batch_add_dm_write(..., false)" should not be used. */ - const size_t finished_scans = batch->used_scans; + const size_t finished_scans = riscv_batch_finished_scans(batch); decrement_reset_delays_counter(target, finished_scans); if (riscv_batch_was_batch_busy(batch)) return increase_dmi_busy_delay(target); @@ -2582,8 +2530,6 @@ static int batch_run(struct target *target, struct riscv_batch *batch) static int batch_run_timeout(struct target *target, struct riscv_batch *batch) { RISCV013_INFO(info); - select_dmi(target->tap); - riscv_batch_add_nop(batch); size_t finished_scans = 0; int64_t then = timeval_ms() + 1000 * riscv_get_command_timeout_sec(); @@ -2603,7 +2549,6 @@ static int batch_run_timeout(struct target *target, struct riscv_batch *batch) decrement_reset_delays_counter(target, new_finished_scans - finished_scans); finished_scans = new_finished_scans; if (!riscv_batch_was_batch_busy(batch)) { - assert(finished_scans == batch->used_scans); return ERROR_OK; } result = increase_dmi_busy_delay(target); @@ -2951,9 +2896,9 @@ static int init_target(struct command_context *cmd_ctx, static int assert_reset(struct target *target) { RISCV013_INFO(info); - int result; - - select_dmi(target->tap); + int result = riscv_dmi_prepare_access(target); + if (result != ERROR_OK) + return result; if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) { /* Run the user-supplied script if there is one. */ @@ -3005,9 +2950,10 @@ static int deassert_reset(struct target *target) dm013_info_t *dm = get_dm(target); if (!dm) return ERROR_FAIL; - int result; - select_dmi(target->tap); + int result = riscv_dmi_prepare_access(target); + if (result != ERROR_OK) + return result; /* Clear the reset, but make sure haltreq is still set */ uint32_t control = 0; control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); @@ -3449,14 +3395,17 @@ static int read_memory_bus_v1(struct target *target, const struct riscv_mem_acce for (uint32_t i = (next_address - address) / size; i < count - 1; i++) { const uint32_t size_in_words = DIV_ROUND_UP(size, 4); struct riscv_batch *batch = riscv_batch_alloc(target, size_in_words); + size_t read_keys[ARRAY_SIZE(sbdata)]; /* Read of sbdata0 must be performed as last because it * starts the new bus data transfer * (in case "sbcs.sbreadondata" was set above). * We don't want to start the next bus read before we * fetch all the data from the last bus read. */ for (uint32_t j = size_in_words - 1; j > 0; --j) - riscv_batch_add_dm_read(batch, sbdata[j], RISCV_DELAY_BASE); - riscv_batch_add_dm_read(batch, sbdata[0], RISCV_DELAY_SYSBUS_READ); + read_keys[j] = riscv_batch_add_dm_read(batch, sbdata[j], + RISCV_DELAY_BASE); + read_keys[0] = riscv_batch_add_dm_read(batch, sbdata[0], + RISCV_DELAY_SYSBUS_READ); int res = batch_run_timeout(target, batch); if (res != ERROR_OK) { @@ -3464,9 +3413,8 @@ static int read_memory_bus_v1(struct target *target, const struct riscv_mem_acce return res; } - const size_t last_key = batch->read_keys_used - 1; - for (size_t k = 0; k <= last_key; ++k) { - sbvalue[k] = riscv_batch_get_dmi_read_data(batch, last_key - k); + for (size_t k = 0; k < size_in_words; ++k) { + sbvalue[k] = riscv_batch_get_dmi_read_data(batch, read_keys[k]); buf_set_u32(buffer + i * size + k * 4, 0, MIN(32, 8 * size), sbvalue[k]); } @@ -4494,7 +4442,8 @@ read_memory_progbuf(struct target *target, const struct riscv_mem_access_args ar { assert(riscv_mem_access_is_read(args)); - select_dmi(target->tap); + if (riscv_dmi_prepare_access(target) != ERROR_OK) + return mem_access_result(MEM_ACCESS_FAILED); memset(args.read_buffer, 0, args.count * args.size); if (execute_autofence(target) != ERROR_OK) diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c index 30094b34b5..a5a7958327 100644 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -13,12 +13,15 @@ #include "target/target.h" #include "target/algorithm.h" #include "target/target_type.h" +#include "target/arm_adi_v5.h" #include <target/smp.h> #include "jtag/jtag.h" #include "target/register.h" #include "target/breakpoints.h" #include "helper/base64.h" #include "helper/time_support.h" +#include "dmi.h" +#include "dtm.h" #include "riscv.h" #include "riscv_reg.h" #include "program.h" @@ -461,6 +464,7 @@ static struct target_type *get_target_type(struct target *target) } RISCV_INFO(info); + switch (info->dtm_version) { case DTM_DTMCS_VERSION_0_11: return &riscv011_target; @@ -477,7 +481,7 @@ static struct target_type *get_target_type(struct target *target) static struct riscv_private_config *alloc_default_riscv_private_config(void) { - struct riscv_private_config * const config = malloc(sizeof(*config)); + struct riscv_private_config * const config = calloc(1, sizeof(*config)); if (!config) { LOG_ERROR("Out of memory!"); return NULL; @@ -505,6 +509,8 @@ static int riscv_create_target(struct target *target) return ERROR_FAIL; } riscv_info_init(target, target->arch_info); + RISCV_INFO(info); + info->dtm = config->dtm; return ERROR_OK; } @@ -611,13 +617,27 @@ static int jim_report_ebreak_config(const struct riscv_private_config *config, return JIM_OK; } +static struct adiv5_private_config *riscv_get_adiv5_config(struct riscv_private_config *config) +{ + if (!config->adiv5_config) { + config->adiv5_config = calloc(1, sizeof(*config->adiv5_config)); + if (!config->adiv5_config) + return NULL; + config->adiv5_config->ap_num = DP_APSEL_INVALID; + } + + return config->adiv5_config; +} + enum riscv_cfg_opts { RISCV_CFG_EBREAK, + RISCV_CFG_DTM, RISCV_CFG_INVALID = -1 }; static struct jim_nvp nvp_config_opts[] = { { .name = "-ebreak", .value = RISCV_CFG_EBREAK }, + { .name = "-dtm", .value = RISCV_CFG_DTM }, { .name = NULL, .value = RISCV_CFG_INVALID } }; @@ -637,8 +657,23 @@ static int riscv_jim_configure(struct target *target, struct jim_nvp *n; int e = jim_nvp_name2value_obj(goi->interp, nvp_config_opts, goi->argv[0], &n); - if (e != JIM_OK) - return JIM_CONTINUE; + if (e != JIM_OK) { + struct adiv5_private_config *adiv5_config = + riscv_get_adiv5_config(config); + if (!adiv5_config) + return JIM_ERR; + + int adiv5_result = adiv5_jim_configure_ext(target, goi, + adiv5_config, ADI_CONFIGURE_DAP_OPTIONAL); + if (adiv5_result == JIM_OK && goi->is_configure + && adiv5_config->dap + && adiv5_config->ap_num != DP_APSEL_INVALID) { + if (riscv_dtm_assign_ap_target(target, adiv5_config->dap, + adiv5_config->ap_num) != ERROR_OK) + return JIM_ERR; + } + return adiv5_result; + } e = jim_getopt_obj(goi, NULL); if (e != JIM_OK) @@ -654,6 +689,27 @@ static int riscv_jim_configure(struct target *target, return goi->is_configure ? jim_configure_ebreak(config, goi) : jim_report_ebreak_config(config, goi->interp); + case RISCV_CFG_DTM: + if (goi->is_configure) { + Jim_Obj *dtm_obj; + e = jim_getopt_obj(goi, &dtm_obj); + if (e != JIM_OK) + return e; + struct riscv_dtm *dtm = riscv_dtm_by_jim_obj(goi->interp, dtm_obj); + if (!dtm) { + Jim_SetResultString(goi->interp, "RISC-V DTM not found", -1); + return JIM_ERR; + } + if (riscv_dtm_assign_target(target, dtm) != ERROR_OK) + return JIM_ERR; + return JIM_OK; + } + if (!config->dtm) { + Jim_SetResultString(goi->interp, "", -1); + return JIM_OK; + } + Jim_SetResultString(goi->interp, riscv_dtm_name(config->dtm), -1); + return JIM_OK; default: assert(false && "'jim_getopt_nvp' should have returned an error."); } @@ -668,29 +724,37 @@ static int riscv_init_target(struct command_context *cmd_ctx, info->cmd_ctx = cmd_ctx; info->reset_delays_wait = -1; - select_dtmcontrol.num_bits = target->tap->ir_length; - select_dbus.num_bits = target->tap->ir_length; - select_idcode.num_bits = target->tap->ir_length; + riscv_semihosting_init(target); + + target->debug_reason = DBG_REASON_DBGRQ; + + return ERROR_OK; +} + +int riscv_dmi_jtag_init_tap(struct jtag_tap *tap) +{ + if (!tap) + return ERROR_FAIL; + + select_dtmcontrol.num_bits = tap->ir_length; + select_dbus.num_bits = tap->ir_length; + select_idcode.num_bits = tap->ir_length; if (bscan_tunnel_ir_width != 0) { uint32_t ir_user4_raw = bscan_tunnel_ir_id; - /* Provide a default value which target some Xilinx FPGA USER4 IR */ + /* Provide a default value which targets some Xilinx FPGA USER4 IR. */ if (ir_user4_raw == 0) { - assert(target->tap->ir_length >= 6); - ir_user4_raw = 0x23 << (target->tap->ir_length - 6); + assert(tap->ir_length >= 6); + ir_user4_raw = 0x23 << (tap->ir_length - 6); } h_u32_to_le(ir_user4, ir_user4_raw); - select_user4.num_bits = target->tap->ir_length; + select_user4.num_bits = tap->ir_length; if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) bscan_tunnel_data_register_select_dmi[1].num_bits = bscan_tunnel_ir_width; else /* BSCAN_TUNNEL_NESTED_TAP */ bscan_tunnel_nested_tap_select_dmi[2].num_bits = bscan_tunnel_ir_width; } - riscv_semihosting_init(target); - - target->debug_reason = DBG_REASON_DBGRQ; - return ERROR_OK; } @@ -717,6 +781,9 @@ static void riscv_deinit_target(struct target *target) { LOG_TARGET_DEBUG(target, "riscv_deinit_target()"); + struct riscv_private_config *config = target->private_config; + if (config) + free(config->adiv5_config); free(target->private_config); struct riscv_info *info = target->arch_info; @@ -2476,17 +2543,15 @@ static int riscv_examine(struct target *target) LOG_TARGET_DEBUG(target, "Target was already examined."); return ERROR_OK; } - - /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ - RISCV_INFO(info); - uint32_t dtmcontrol; - if (dtmcs_scan(target->tap, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) { - LOG_TARGET_ERROR(target, "Could not read dtmcontrol. Check JTAG connectivity/board power."); + if (riscv_dmi_select(target) != ERROR_OK) return ERROR_FAIL; - } - LOG_TARGET_DEBUG(target, "dtmcontrol=0x%" PRIx32, dtmcontrol); - uint32_t dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION); + + struct riscv_dmi_info dmi_info; + if (riscv_dmi_get_info(target, &dmi_info) != ERROR_OK) + return ERROR_FAIL; + + uint32_t dtm_version = dmi_info.dtm_version; LOG_TARGET_DEBUG(target, "version=0x%" PRIx32, dtm_version); struct target_type *tt; @@ -4797,22 +4862,17 @@ COMMAND_HANDLER(riscv_reset_delays) struct target *target = get_current_target(CMD_CTX); RISCV_INFO(r); r->reset_delays_wait = wait; + riscv_dtm_reset_delays(r->dtm, wait); return ERROR_OK; } -COMMAND_HANDLER(riscv_set_ir) +int riscv_dmi_jtag_set_ir(const char *ir_name, uint32_t value) { - if (CMD_ARGC != 2) - return ERROR_COMMAND_SYNTAX_ERROR; - - uint32_t value; - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); - - if (!strcmp(CMD_ARGV[0], "idcode")) + if (!strcmp(ir_name, "idcode")) buf_set_u32(ir_idcode, 0, 32, value); - else if (!strcmp(CMD_ARGV[0], "dtmcs")) + else if (!strcmp(ir_name, "dtmcs")) buf_set_u32(ir_dtmcontrol, 0, 32, value); - else if (!strcmp(CMD_ARGV[0], "dmi")) + else if (!strcmp(ir_name, "dmi")) buf_set_u32(ir_dbus, 0, 32, value); else return ERROR_FAIL; @@ -4820,6 +4880,17 @@ COMMAND_HANDLER(riscv_set_ir) return ERROR_OK; } +COMMAND_HANDLER(riscv_set_ir) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + uint32_t value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + return riscv_dtm_set_ir(CMD_ARGV[0], value); +} + COMMAND_HANDLER(riscv_resume_order) { if (CMD_ARGC != 1) @@ -4837,6 +4908,20 @@ COMMAND_HANDLER(riscv_resume_order) return ERROR_OK; } +int riscv_dmi_jtag_use_bscan_tunnel(uint8_t irwidth, int tunnel_type) +{ + if (tunnel_type == BSCAN_TUNNEL_NESTED_TAP) + LOG_INFO("Nested Tap based Bscan Tunnel Selected"); + else if (tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) + LOG_INFO("Simple Register based Bscan Tunnel Selected"); + else + LOG_INFO("Invalid Tunnel type selected ! : selecting default Nested Tap Type"); + + bscan_tunnel_type = tunnel_type; + bscan_tunnel_ir_width = irwidth; + return ERROR_OK; +} + COMMAND_HANDLER(riscv_use_bscan_tunnel) { uint8_t irwidth = 0; @@ -4845,26 +4930,23 @@ COMMAND_HANDLER(riscv_use_bscan_tunnel) if (CMD_ARGC < 1 || CMD_ARGC > 2) return ERROR_COMMAND_SYNTAX_ERROR; - if (CMD_ARGC >= 1) { - COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], irwidth); - assert(BSCAN_TUNNEL_IR_WIDTH_NBITS < 8); - if (irwidth >= (uint8_t)1 << BSCAN_TUNNEL_IR_WIDTH_NBITS) { - command_print(CMD, "'value' does not fit into %d bits.", - BSCAN_TUNNEL_IR_WIDTH_NBITS); - return ERROR_COMMAND_ARGUMENT_OVERFLOW; - } + COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], irwidth); + assert(BSCAN_TUNNEL_IR_WIDTH_NBITS < 8); + if (irwidth >= (uint8_t)1 << BSCAN_TUNNEL_IR_WIDTH_NBITS) { + command_print(CMD, "'value' does not fit into %d bits.", + BSCAN_TUNNEL_IR_WIDTH_NBITS); + return ERROR_COMMAND_ARGUMENT_OVERFLOW; } if (CMD_ARGC == 2) COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tunnel_type); - if (tunnel_type == BSCAN_TUNNEL_NESTED_TAP) - LOG_INFO("Nested Tap based Bscan Tunnel Selected"); - else if (tunnel_type == BSCAN_TUNNEL_DATA_REGISTER) - LOG_INFO("Simple Register based Bscan Tunnel Selected"); - else - LOG_INFO("Invalid Tunnel type selected ! : selecting default Nested Tap Type"); - bscan_tunnel_type = tunnel_type; - bscan_tunnel_ir_width = irwidth; + return riscv_dtm_use_bscan_tunnel(irwidth, tunnel_type); +} + +int riscv_dmi_jtag_set_bscan_tunnel_ir(int ir_id) +{ + LOG_INFO("Bscan tunnel IR 0x%x selected", ir_id); + bscan_tunnel_ir_id = ir_id; return ERROR_OK; } @@ -4878,10 +4960,7 @@ COMMAND_HANDLER(riscv_set_bscan_tunnel_ir) if (CMD_ARGC == 1) COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ir_id); - LOG_INFO("Bscan tunnel IR 0x%x selected", ir_id); - - bscan_tunnel_ir_id = ir_id; - return ERROR_OK; + return riscv_dtm_set_bscan_tunnel_ir(ir_id); } COMMAND_HANDLER(riscv_set_maskisr) diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 2a0a9b95f0..fdc353e026 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -4,6 +4,9 @@ #define OPENOCD_TARGET_RISCV_RISCV_H struct riscv_program; +struct riscv_dmi_backend_ops; +struct riscv_dtm; +struct adiv5_private_config; #include <stdint.h> #include "opcodes.h" @@ -169,6 +172,7 @@ struct riscv_info { unsigned int common_magic; unsigned int dtm_version; + struct riscv_dtm *dtm; struct command_context *cmd_ctx; void *version_specific; @@ -378,6 +382,8 @@ enum riscv_priv_mode { struct riscv_private_config { bool dcsr_ebreak_fields[N_RISCV_MODE]; + struct riscv_dtm *dtm; + struct adiv5_private_config *adiv5_config; }; static inline struct riscv_private_config diff --git a/src/target/target.c b/src/target/target.c index 1920de55ca..f4ea3dceca 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -709,7 +709,7 @@ static int jtag_enable_callback(enum jtag_event event, void *priv) { struct target *target = priv; - if (event != JTAG_TAP_EVENT_ENABLE || !target->tap->enabled) + if (event != JTAG_TAP_EVENT_ENABLE || !target->tap || !target->tap->enabled) return ERROR_OK; jtag_unregister_event_callback(jtag_enable_callback, target); @@ -729,7 +729,7 @@ int target_examine(void) for (target = all_targets; target; target = target->next) { /* defer examination, but don't skip it */ - if (!target->tap->enabled) { + if (target->tap && !target->tap->enabled) { jtag_register_event_callback(jtag_enable_callback, target); continue; @@ -2788,7 +2788,7 @@ static int find_target(struct command_invocation *cmd, const char *name) command_print(cmd, "Target: %s is unknown, try one of:\n", name); return ERROR_FAIL; } - if (!target->tap->enabled) { + if (target->tap && !target->tap->enabled) { command_print(cmd, "Target: TAP %s is disabled, " "can't be the current target\n", target->tap->dotted_name); @@ -2821,7 +2821,9 @@ COMMAND_HANDLER(handle_targets_command) const char *state; char marker = ' '; - if (target->tap->enabled) + if (!target->tap) + state = target_state_name(target); + else if (target->tap->enabled) state = target_state_name(target); else state = "tap-disabled"; @@ -2837,7 +2839,7 @@ COMMAND_HANDLER(handle_targets_command) target_name(target), target_type_name(target), nvp_value2name(nvp_target_endian, target->endianness)->name, - target->tap->dotted_name, + target->tap ? target->tap->dotted_name : "<none>", state); } @@ -2908,7 +2910,7 @@ static int sense_handler(void) static int handle_one_target(struct target *target) { - if (!target_active_polled(target) || !target->tap->enabled) + if (!target_active_polled(target) || (target->tap && !target->tap->enabled)) return ERROR_OK; int res = target_poll(target); @@ -3159,10 +3161,14 @@ COMMAND_HANDLER(handle_poll_command) if (CMD_ARGC == 0) { command_print(CMD, "background polling: %s", jtag_poll_get_enabled() ? "on" : "off"); - command_print(CMD, "TAP: %s (%s)", - target->tap->dotted_name, - target->tap->enabled ? "enabled" : "disabled"); - if (!target->tap->enabled) + if (target->tap) { + command_print(CMD, "TAP: %s (%s)", + target->tap->dotted_name, + target->tap->enabled ? "enabled" : "disabled"); + } else { + command_print(CMD, "TAP: <none>"); + } + if (target->tap && !target->tap->enabled) return ERROR_OK; retval = target_poll(target); if (retval != ERROR_OK) @@ -5331,7 +5337,7 @@ COMMAND_HANDLER(handle_target_examine) } struct target *target = get_current_target(CMD_CTX); - if (!target->tap->enabled) { + if (target->tap && !target->tap->enabled) { command_print(CMD, "[TAP is disabled]"); return ERROR_FAIL; } @@ -5393,7 +5399,7 @@ COMMAND_HANDLER(handle_target_poll) return ERROR_COMMAND_SYNTAX_ERROR; struct target *target = get_current_target(CMD_CTX); - if (!target->tap->enabled) { + if (target->tap && !target->tap->enabled) { command_print(CMD, "[TAP is disabled]"); return ERROR_FAIL; } @@ -5420,7 +5426,7 @@ COMMAND_HANDLER(handle_target_reset) COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], a); struct target *target = get_current_target(CMD_CTX); - if (!target->tap->enabled) { + if (target->tap && !target->tap->enabled) { command_print(CMD, "[TAP is disabled]"); return ERROR_FAIL; } @@ -5454,7 +5460,7 @@ COMMAND_HANDLER(handle_target_halt) return ERROR_COMMAND_SYNTAX_ERROR; struct target *target = get_current_target(CMD_CTX); - if (!target->tap->enabled) { + if (target->tap && !target->tap->enabled) { command_print(CMD, "[TAP is disabled]"); return ERROR_FAIL; } @@ -5477,7 +5483,7 @@ COMMAND_HANDLER(handle_target_wait_state) COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], a); struct target *target = get_current_target(CMD_CTX); - if (!target->tap->enabled) { + if (target->tap && !target->tap->enabled) { command_print(CMD, "[TAP is disabled]"); return ERROR_FAIL; } @@ -5920,14 +5926,20 @@ COMMAND_HANDLER(handle_target_create) command_print(CMD, "-dap ?name? required when creating target"); retval = ERROR_COMMAND_ARGUMENT_INVALID; } + } else if (target->has_dtm) { + if (!target->dtm_configured) { + command_print(CMD, "-dtm ?name? required when creating target"); + retval = ERROR_COMMAND_ARGUMENT_INVALID; + } } else { if (!target->tap_configured) { command_print(CMD, "-chain-position ?name? required when creating target"); retval = ERROR_COMMAND_ARGUMENT_INVALID; } } - /* tap must be set after target was configured */ - if (!target->tap) + /* tap must be set after target was configured, unless the target + * uses a non-JTAG DTM. */ + if (!target->tap && !target->has_dtm) retval = ERROR_COMMAND_ARGUMENT_INVALID; } diff --git a/src/target/target.h b/src/target/target.h index ce1fc728be..48d8f24c5a 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -187,6 +187,8 @@ struct target { * currently. */ bool has_dap; /* set to true if target has ADIv5 support */ bool dap_configured; /* set to true if ADIv5 DAP is configured */ + bool has_dtm; /* set to true if target has RISC-V DTM support */ + bool dtm_configured; /* set to true if RISC-V DTM is configured */ bool tap_configured; /* set to true if JTAG tap has been configured * through -chain-position */ diff --git a/tcl/target/rp2350-riscv.cfg b/tcl/target/rp2350-riscv.cfg new file mode 100644 index 0000000000..7f852a279c --- /dev/null +++ b/tcl/target/rp2350-riscv.cfg @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# RP2350 Hazard3 RISC-V debug over SWD/ADIv6. +# Rescue mode and mixed Cortex-M33/RISC-V switching are intentionally not +# configured here. + +transport select swd + +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME rp2350 +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x00040927 +} + +if { [info exists USE_CORE] } { + set _USE_CORE $USE_CORE +} else { + set _USE_CORE { rv0 rv1 } +} + +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x10000 +} + +if { [info exists FLASHSIZE] } { + set _FLASHSIZE $FLASHSIZE +} else { + set _FLASHSIZE 0 +} + +proc _rp2350_riscv_reset_init { } { + set chip_id [format 0x%08x [read_memory 0x40000000 32 1]] + + # RP2350 A0 needs the QSPI IO isolation removed before ROM flash calls. + switch $chip_id { + 0x00004927 { + mww 0x40030014 0 + mww 0x4003001c 0 + mww 0x40030024 0 + mww 0x4003002c 0 + mww 0x40030034 0 + mww 0x4003003c 0 + } + } + + rp2xxx rom_api_call FC +} + +swj_newdap $_CHIPNAME cpu -expected-id $_CPUTAPID + +if { [info exists SWD_MULTIDROP] } { + dap create $_CHIPNAME.dap -adiv6 -chain-position $_CHIPNAME.cpu \ + -dp-id 0x0040927 -instance-id 0 +} else { + dap create $_CHIPNAME.dap -adiv6 -chain-position $_CHIPNAME.cpu +} + +if { [lsearch $_USE_CORE rv0] >= 0 } { + set _TARGETNAME_RV0 $_CHIPNAME.rv0 + target create $_TARGETNAME_RV0 riscv -dap $_CHIPNAME.dap -ap-num 0xa000 -coreid 0 + $_TARGETNAME_RV0 riscv virt2phys_mode off + $_TARGETNAME_RV0 configure -work-area-phys 0x20010000 \ + -work-area-size $_WORKAREASIZE -work-area-backup 0 +} + +if { [lsearch $_USE_CORE rv1] >= 0 } { + set _TARGETNAME_RV1 $_CHIPNAME.rv1 + target create $_TARGETNAME_RV1 riscv -dap $_CHIPNAME.dap -ap-num 0xa000 -coreid 1 + $_TARGETNAME_RV1 riscv virt2phys_mode off + $_TARGETNAME_RV1 configure -work-area-phys 0x20010000 \ + -work-area-size $_WORKAREASIZE -work-area-backup 0 +} + +if { [info exists USE_SMP] } { + set _USE_SMP $USE_SMP +} else { + set _USE_SMP 0 +} + +if { $_USE_SMP } { + $_TARGETNAME_RV0 configure -rtos hwthread + $_TARGETNAME_RV1 configure -rtos hwthread + target smp $_TARGETNAME_RV0 $_TARGETNAME_RV1 +} + +if { [info exists _TARGETNAME_RV0] } { + set _FLASH_TARGET $_TARGETNAME_RV0 +} elseif { [info exists _TARGETNAME_RV1] } { + set _FLASH_TARGET $_TARGETNAME_RV1 +} + +if { [info exists _FLASH_TARGET] } { + # QSPI flash size detection during GDB connect requires backing up RAM. + set _WKA_BACKUP [expr { $_FLASHSIZE == 0 }] + $_FLASH_TARGET configure -event reset-init "_rp2350_riscv_reset_init" + $_FLASH_TARGET configure -work-area-phys 0x20010000 \ + -work-area-size $_WORKAREASIZE -work-area-backup $_WKA_BACKUP + + set _FLASHNAME $_CHIPNAME.flash + flash bank $_FLASHNAME rp2xxx 0x10000000 $_FLASHSIZE 0 0 $_FLASH_TARGET +} + +if { [info exists _TARGETNAME_RV1] && [info exists _FLASHNAME] } { + # Alias to ensure GDB connecting to core 1 gets the correct memory map. + flash bank $_CHIPNAME.alias virtual 0x10000000 0 0 0 $_TARGETNAME_RV1 $_FLASHNAME +} + +if { [info exists _TARGETNAME_RV0] } { + targets $_TARGETNAME_RV0 +} elseif { [info exists _TARGETNAME_RV1] } { + targets $_TARGETNAME_RV1 +} --
