Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Cortex-A/R speedups #1752

Merged
merged 14 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/platforms/hosted/cmsis_dap.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ static void dap_mem_read(adiv5_access_port_s *ap, void *dest, uint32_t src, size
if (len == 0)
return;
const align_e align = MIN_ALIGN(src, len);
DEBUG_WIRE("dap_mem_read @ %" PRIx32 " len %zu, align %d\n", src, len, align);
DEBUG_PROBE("dap_mem_read @ %" PRIx32 " len %zu, align %d\n", src, len, align);
/* If the read can be done in a single transaction, use the dap_read_single() fast-path */
if ((1U << align) == len) {
dap_read_single(ap, dest, src, align);
Expand All @@ -566,7 +566,7 @@ static void dap_mem_read(adiv5_access_port_s *ap, void *dest, uint32_t src, size
/* blocks - i gives how many blocks are left to transfer in this 1024 byte chunk */
const size_t transfer_length = MIN(blocks - i, blocks_per_transfer) << align;
if (!dap_read_block(ap, data + offset, src + offset, transfer_length, align)) {
DEBUG_WIRE("mem_read failed: %u\n", ap->dp->fault);
DEBUG_WIRE("dap_mem_read failed: %u\n", ap->dp->fault);
return;
}
offset += transfer_length;
Expand All @@ -579,7 +579,7 @@ static void dap_mem_write(adiv5_access_port_s *ap, uint32_t dest, const void *sr
{
if (len == 0)
return;
DEBUG_WIRE("memwrite @ %" PRIx32 " len %zu, align %d\n", dest, len, align);
DEBUG_PROBE("dap_mem_write @ %" PRIx32 " len %zu, align %d\n", dest, len, align);
/* If the write can be done in a single transaction, use the dap_write_single() fast-path */
if ((1U << align) == len) {
dap_write_single(ap, dest, src, align);
Expand All @@ -603,13 +603,13 @@ static void dap_mem_write(adiv5_access_port_s *ap, uint32_t dest, const void *sr
/* blocks - i gives how many blocks are left to transfer in this 1024 byte chunk */
const size_t transfer_length = MIN(blocks - i, blocks_per_transfer) << align;
if (!dap_write_block(ap, dest + offset, data + offset, transfer_length, align)) {
DEBUG_WIRE("mem_write failed: %u\n", ap->dp->fault);
DEBUG_WIRE("dap_mem_write failed: %u\n", ap->dp->fault);
return;
}
offset += transfer_length;
}
}
DEBUG_WIRE("dap_mem_write_sized transferred %zu blocks\n", len >> align);
DEBUG_WIRE("dap_dap_mem_write transferred %zu blocks\n", len >> align);

/* Make sure this write is complete by doing a dummy read */
adiv5_dp_read(ap->dp, ADIV5_DP_RDBUFF);
Expand Down
4 changes: 3 additions & 1 deletion src/target/adiv5.c
Original file line number Diff line number Diff line change
Expand Up @@ -1021,8 +1021,10 @@ void ap_mem_access_setup(adiv5_access_port_s *ap, uint32_t addr, align_e align)
csw |= ADIV5_AP_CSW_SIZE_WORD;
break;
}
/* Select AP bank 0 and write CSW */
adiv5_ap_write(ap, ADIV5_AP_CSW, csw);
adiv5_dp_low_access(ap->dp, ADIV5_LOW_WRITE, ADIV5_AP_TAR, addr);
/* Then write TAR which is in the same AP bank */
adiv5_dp_write(ap->dp, ADIV5_AP_TAR, addr);
}

/* Unpack data from the source uint32_t value based on data alignment and source address */
Expand Down
2 changes: 2 additions & 0 deletions src/target/adiv5.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@
/* Constants for the DP's quirks field */
#define ADIV5_DP_QUIRK_MINDP (1U << 0U) /* DP is a minimal DP implementation */
#define ADIV5_DP_QUIRK_DUPED_AP (1U << 1U) /* DP has only 1 AP but the address decoding is bugged */
/* This one is not a quirk, but the field's a convinient place to store this */
#define ADIV5_AP_ACCESS_BANKED (1U << 7U) /* Last AP access was done using the banked interface */

typedef struct adiv5_access_port adiv5_access_port_s;
typedef struct adiv5_debug_port adiv5_debug_port_s;
Expand Down
2 changes: 2 additions & 0 deletions src/target/cortex.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ uint32_t cortex_dbg_read32(target_s *const target, const uint16_t src)
/* Translate the offset given in the src parameter into an address in the debug address space and read */
const cortex_priv_s *const priv = (cortex_priv_s *)target->priv;
adiv5_access_port_s *const ap = cortex_ap(target);
ap->dp->quirks &= ~ADIV5_AP_ACCESS_BANKED;
uint32_t result = 0;
adiv5_mem_read(ap, &result, priv->base_addr + src, sizeof(result));
return result;
Expand All @@ -76,6 +77,7 @@ void cortex_dbg_write32(target_s *const target, const uint16_t dest, const uint3
/* Translate the offset given int he dest parameter into an address int he debug address space and write */
const cortex_priv_s *const priv = (cortex_priv_s *)target->priv;
adiv5_access_port_s *const ap = cortex_ap(target);
ap->dp->quirks &= ~ADIV5_AP_ACCESS_BANKED;
adiv5_mem_write(ap, priv->base_addr + dest, &value, sizeof(value));
}

Expand Down
163 changes: 123 additions & 40 deletions src/target/cortexar.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ typedef struct cortexar_priv {
#define CORTEXAR_DBG_DSCR_INTERRUPT_DISABLE (1U << 11U)
#define CORTEXAR_DBG_DSCR_ITR_ENABLE (1U << 13U)
#define CORTEXAR_DBG_DSCR_HALTING_DBG_ENABLE (1U << 14U)
#define CORTEXAR_DBG_DCSR_DCC_MASK 0x00300000U
#define CORTEXAR_DBG_DCSR_DCC_NORMAL 0x00000000U
#define CORTEXAR_DBG_DCSR_DCC_STALL 0x00100000U
#define CORTEXAR_DBG_DCSR_DCC_FAST 0x00200000U
#define CORTEXAR_DBG_DSCR_INSN_COMPLETE (1U << 24U)
#define CORTEXAR_DBG_DSCR_DTR_READ_READY (1U << 29U)
#define CORTEXAR_DBG_DSCR_DTR_WRITE_DONE (1U << 30U)
Expand Down Expand Up @@ -190,6 +194,14 @@ typedef struct cortexar_priv {
#define CORTEXAR_CPSR_MODE_SYS 0x0000001fU
#define CORTEXAR_CPSR_THUMB (1U << 5U)

/* Banked register offsets for when using using the DB{0,3} interface */
enum {
CORTEXAR_BANKED_DTRTX,
CORTEXAR_BANKED_ITR,
CORTEXAR_BANKED_DCSR,
CORTEXAR_BANKED_DTRRX
};

/*
* Table of encodings for the banked SPSRs - These are encoded in the following format:
* Bits[0]: SYSm[0]
Expand Down Expand Up @@ -385,14 +397,20 @@ void cortexar_detach(target_s *target);

static const char *cortexar_target_description(target_s *target);

static bool cortexar_run_insn(target_s *const target, const uint32_t insn)
static void cortexar_banked_dcc_mode(target_s *const target)
{
cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
if (!(priv->base.ap->dp->quirks & ADIV5_AP_ACCESS_BANKED)) {
priv->base.ap->dp->quirks |= ADIV5_AP_ACCESS_BANKED;
/* Configure the AP to put {DBGDTR{TX,RX},DBGITR,DBGDCSR} in banked data registers window */
ap_mem_access_setup(priv->base.ap, priv->base.base_addr + CORTEXAR_DBG_DTRTX, ALIGN_32BIT);
/* Selecting AP bank 1 to finish switching into banked mode */
adiv5_dp_write(priv->base.ap->dp, ADIV5_DP_SELECT, ((uint32_t)priv->base.ap->apsel << 24U) | 0x10U);
}
}

static bool cortexar_check_data_abort(target_s *const target, const uint32_t status)
{
/* Issue the requested instruction to the core */
cortex_dbg_write32(target, CORTEXAR_DBG_ITR, insn);
/* Poll for the instruction to complete */
uint32_t status = 0;
while (!(status & CORTEXAR_DBG_DSCR_INSN_COMPLETE))
status = cortex_dbg_read32(target, CORTEXAR_DBG_DSCR);
/* If the instruction triggered a synchronous data abort, signal failure having cleared it */
if (status & CORTEXAR_DBG_DSCR_SYNC_DATA_ABORT) {
cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
Expand All @@ -402,49 +420,62 @@ static bool cortexar_run_insn(target_s *const target, const uint32_t insn)
return !(status & CORTEXAR_DBG_DSCR_SYNC_DATA_ABORT);
}

static bool cortexar_run_insn(target_s *const target, const uint32_t insn)
{
const cortexar_priv_s *const priv = (const cortexar_priv_s *)target->priv;
/* Make sure we're in banked mode */
cortexar_banked_dcc_mode(target);
/* Issue the requested instruction to the core */
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_ITR), insn);
/* Poll for the instruction to complete */
uint32_t status = 0;
while (!(status & CORTEXAR_DBG_DSCR_INSN_COMPLETE))
status = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR));
/* Check if the instruction triggered a synchronous data abort */
return cortexar_check_data_abort(target, status);
}

static bool cortexar_run_read_insn(target_s *const target, const uint32_t insn, uint32_t *const result)
{
const cortexar_priv_s *const priv = (const cortexar_priv_s *)target->priv;
/* Make sure we're in banked mode */
cortexar_banked_dcc_mode(target);
/* Issue the requested instruction to the core */
cortex_dbg_write32(target, CORTEXAR_DBG_ITR, insn);
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_ITR), insn);
/* Poll for the instruction to complete and the data to become ready in the DTR */
uint32_t status = 0;
while ((status & (CORTEXAR_DBG_DSCR_INSN_COMPLETE | CORTEXAR_DBG_DSCR_DTR_READ_READY)) !=
(CORTEXAR_DBG_DSCR_INSN_COMPLETE | CORTEXAR_DBG_DSCR_DTR_READ_READY)) {
status = cortex_dbg_read32(target, CORTEXAR_DBG_DSCR);
/* If the instruction triggered a synchronous data abort, signal failure having cleared it */
if (status & CORTEXAR_DBG_DSCR_SYNC_DATA_ABORT) {
cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
priv->core_status |= CORTEXAR_STATUS_DATA_FAULT;
cortex_dbg_write32(target, CORTEXAR_DBG_DRCR, CORTEXAR_DBG_DRCR_CLR_STICKY_EXC);
status = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR));
/* Check if the instruction triggered a synchronous data abort */
if (!cortexar_check_data_abort(target, status))
return false;
}
}
/* Read back the DTR to complete the read and signal success */
*result = cortex_dbg_read32(target, CORTEXAR_DBG_DTRRX);
*result = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DTRRX));
return true;
}

static bool cortexar_run_write_insn(target_s *const target, const uint32_t insn, const uint32_t data)
{
const cortexar_priv_s *const priv = (const cortexar_priv_s *)target->priv;
/* Make sure we're in banked mode */
cortexar_banked_dcc_mode(target);
/* Set up the data in the DTR for the transaction */
cortex_dbg_write32(target, CORTEXAR_DBG_DTRTX, data);
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DTRTX), data);
/* Poll for the data to become ready in the DTR */
while (!(cortex_dbg_read32(target, CORTEXAR_DBG_DSCR) & CORTEXAR_DBG_DSCR_DTR_WRITE_DONE))
while (!(adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR)) & CORTEXAR_DBG_DSCR_DTR_WRITE_DONE))
continue;
/* Issue the requested instruction to the core */
cortex_dbg_write32(target, CORTEXAR_DBG_ITR, insn);
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_ITR), insn);
/* Poll for the instruction to complete and the data to be consumed from the DTR */
uint32_t status = 0;
while ((status & (CORTEXAR_DBG_DSCR_INSN_COMPLETE | CORTEXAR_DBG_DSCR_DTR_WRITE_DONE)) !=
CORTEXAR_DBG_DSCR_INSN_COMPLETE) {
status = cortex_dbg_read32(target, CORTEXAR_DBG_DSCR);
/* If the instruction triggered a synchronous data abort, signal failure having cleared it */
if (status & CORTEXAR_DBG_DSCR_SYNC_DATA_ABORT) {
cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
priv->core_status |= CORTEXAR_STATUS_DATA_FAULT;
cortex_dbg_write32(target, CORTEXAR_DBG_DRCR, CORTEXAR_DBG_DRCR_CLR_STICKY_EXC);
status = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR));
/* Check if the instruction triggered a synchronous data abort */
if (!cortexar_check_data_abort(target, status))
return false;
}
}
return true;
}
Expand Down Expand Up @@ -942,8 +973,37 @@ static bool cortexar_check_error(target_s *const target)
}

/* Fast path for cortexar_mem_read(). Assumes the address to read data from is already loaded in r0. */
static inline bool cortexr_mem_read_fast(target_s *const target, uint32_t *const dest, const size_t count)
static inline bool cortexar_mem_read_fast(target_s *const target, uint32_t *const dest, const size_t count)
{
/* If we need to read more than a couple of uint32_t's, DCC Fast mode makes more sense, so use it. */
if (count > 2U) {
cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
/* Make sure we're banked mode */
cortexar_banked_dcc_mode(target);
/* Switch into DCC Fast mode */
const uint32_t dbg_dcsr =
adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR)) & ~CORTEXAR_DBG_DCSR_DCC_MASK;
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR), dbg_dcsr | CORTEXAR_DBG_DCSR_DCC_FAST);
/* Set up continual load so we can hammer the DTR */
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_ITR), ARM_LDC_R0_POSTINC4_DTRTX_INSN);
/* Run the transfer, hammering the DTR */
for (size_t offset = 0; offset < count; ++offset) {
/* Read the next value, which is the value for the last instruction run */
const uint32_t value = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DTRRX));
/* If we've run the instruction at least once, store it */
if (offset)
dest[offset - 1U] = value;
}
/* Now read out the status from the DCSR in case anything went wrong */
const uint32_t status = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR));
/* Go back into DCC Normal (Non-blocking) mode */
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR), dbg_dcsr | CORTEXAR_DBG_DCSR_DCC_NORMAL);
/* Grab the value of the last instruction run now it won't run again */
dest[count - 1U] = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DTRRX));
/* Check if the instruction triggered a synchronous data abort */
return cortexar_check_data_abort(target, status);
}

/* Read each of the uint32_t's checking for failure */
for (size_t offset = 0; offset < count; ++offset) {
if (!cortexar_run_read_insn(target, ARM_LDC_R0_POSTINC4_DTRTX_INSN, dest + offset))
Expand All @@ -953,7 +1013,7 @@ static inline bool cortexr_mem_read_fast(target_s *const target, uint32_t *const
}

/* Slow path for cortexar_mem_read(). Trashes r0 and r1. */
static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, target_addr_t addr, const size_t length)
static bool cortexar_mem_read_slow(target_s *const target, uint8_t *const data, target_addr_t addr, const size_t length)
{
size_t offset = 0;
/* If the address is odd, read a byte to get onto an even address */
Expand All @@ -971,7 +1031,7 @@ static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, t
offset += 2U;
}
/* Use the fast path to read as much as possible before doing a slow path fixup at the end */
if (!cortexr_mem_read_fast(target, (uint32_t *)(data + offset), (length - offset) >> 2U))
if (!cortexar_mem_read_fast(target, (uint32_t *)(data + offset), (length - offset) >> 2U))
return false;
const uint8_t remainder = (length - offset) & 3U;
/* If the remainder needs at least 2 more bytes read, do this first */
Expand All @@ -990,7 +1050,7 @@ static bool cortexr_mem_read_slow(target_s *const target, uint8_t *const data, t
return true; /* Signal success */
}

static void cortexr_mem_handle_fault(target_s *const target, const char *const func)
static void cortexar_mem_handle_fault(target_s *const target, const char *const func)
{
const cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
/* If we suffered a fault of some kind, grab the reason and restore DFSR/DFAR */
Expand Down Expand Up @@ -1029,11 +1089,11 @@ static void cortexar_mem_read(target_s *const target, void *const dest, const ta

/* If the address is 32-bit aligned and we're reading 32 bits at a time, use the fast path */
if ((src & 3U) == 0U && (len & 3U) == 0U)
cortexr_mem_read_fast(target, (uint32_t *)dest, len >> 2U);
cortexar_mem_read_fast(target, (uint32_t *)dest, len >> 2U);
else
cortexr_mem_read_slow(target, (uint8_t *)dest, src, len);
cortexar_mem_read_slow(target, (uint8_t *)dest, src, len);
/* Deal with any data faults that occurred */
cortexr_mem_handle_fault(target, __func__);
cortexar_mem_handle_fault(target, __func__);

DEBUG_PROTO("%s: Reading %zu bytes @0x%" PRIx32 ":", __func__, len, src);
#ifndef DEBUG_PROTO_IS_NOOP
Expand All @@ -1050,9 +1110,31 @@ static void cortexar_mem_read(target_s *const target, void *const dest, const ta
}

/* Fast path for cortexar_mem_write(). Assumes the address to read data from is already loaded in r0. */
static inline bool cortexr_mem_write_fast(target_s *const target, const uint32_t *const src, const size_t count)
static inline bool cortexar_mem_write_fast(target_s *const target, const uint32_t *const src, const size_t count)
{
/* Read each of the uint32_t's checking for failure */
/* If we need to write more than a couple of uint32_t's, DCC Fast mode makes more sense, so use it. */
if (count > 2U) {
cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
/* Make sure we're banked mode */
cortexar_banked_dcc_mode(target);
/* Switch into DCC Fast mode */
const uint32_t dbg_dcsr =
adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR)) & ~CORTEXAR_DBG_DCSR_DCC_MASK;
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR), dbg_dcsr | CORTEXAR_DBG_DCSR_DCC_FAST);
/* Set up continual store so we can hammer the DTR */
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_ITR), ARM_STC_DTRRX_R0_POSTINC4_INSN);
/* Run the transfer, hammering the DTR */
for (size_t offset = 0; offset < count; ++offset)
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DTRTX), src[offset]);
/* Now read out the status from the DCSR in case anything went wrong */
const uint32_t status = adiv5_dp_read(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR));
/* Go back into DCC Normal (Non-blocking) mode */
adiv5_dp_write(priv->base.ap->dp, ADIV5_AP_DB(CORTEXAR_BANKED_DCSR), dbg_dcsr | CORTEXAR_DBG_DCSR_DCC_NORMAL);
/* Check if the instruction triggered a synchronous data abort */
return cortexar_check_data_abort(target, status);
}

/* Write each of the uint32_t's checking for failure */
for (size_t offset = 0; offset < count; ++offset) {
if (!cortexar_run_write_insn(target, ARM_STC_DTRRX_R0_POSTINC4_INSN, src[offset]))
return false; /* Propagate failure if it happens */
Expand All @@ -1061,7 +1143,7 @@ static inline bool cortexr_mem_write_fast(target_s *const target, const uint32_t
}

/* Slow path for cortexar_mem_write(). Trashes r0 and r1. */
static bool cortexr_mem_write_slow(
static bool cortexar_mem_write_slow(
target_s *const target, target_addr_t addr, const uint8_t *const data, const size_t length)
{
size_t offset = 0;
Expand All @@ -1080,7 +1162,7 @@ static bool cortexr_mem_write_slow(
offset += 2U;
}
/* Use the fast path to write as much as possible before doing a slow path fixup at the end */
if (!cortexr_mem_write_fast(target, (uint32_t *)(data + offset), (length - offset) >> 2U))
if (!cortexar_mem_write_fast(target, (uint32_t *)(data + offset), (length - offset) >> 2U))
return false;
const uint8_t remainder = (length - offset) & 3U;
/* If the remainder needs at least 2 more bytes write, do this first */
Expand Down Expand Up @@ -1135,11 +1217,11 @@ static void cortexar_mem_write(

/* If the address is 32-bit aligned and we're writing 32 bits at a time, use the fast path */
if ((dest & 3U) == 0U && (len & 3U) == 0U)
cortexr_mem_write_fast(target, (const uint32_t *)src, len >> 2U);
cortexar_mem_write_fast(target, (const uint32_t *)src, len >> 2U);
else
cortexr_mem_write_slow(target, dest, (const uint8_t *)src, len);
cortexar_mem_write_slow(target, dest, (const uint8_t *)src, len);
/* Deal with any data faults that occurred */
cortexr_mem_handle_fault(target, __func__);
cortexar_mem_handle_fault(target, __func__);
}

static void cortexar_regs_read(target_s *const target, void *const data)
Expand Down Expand Up @@ -1346,6 +1428,7 @@ static target_halt_reason_e cortexar_halt_poll(target_s *const target, target_ad
static void cortexar_halt_resume(target_s *const target, const bool step)
{
cortexar_priv_s *const priv = (cortexar_priv_s *)target->priv;
priv->base.ap->dp->quirks &= ~ADIV5_AP_ACCESS_BANKED;
/* Restore the core's registers so the running program doesn't know we've been in there */
cortexar_regs_restore(target);

Expand Down
Loading
Loading