作為ARM嵌入式工程師,下載調(diào)試器都應(yīng)該知道,但你真正了解SWD如何下載調(diào)試的么?ARM 芯片通過什么物理接口和什么方式連接完全取決于芯片Debug子系統(tǒng)的架構(gòu)如何?
你可以從芯片手冊的Debug章節(jié)獲得DAP(Debug Access Port)的信息。我調(diào)試的是NXP公司最新車載芯片S32K3系列的芯片,從S32K3xx Reference Manual中Debug 子系統(tǒng)章節(jié)中得知該芯片的debug和Trace接口基于Arm CoreSight SoC-400的標準。 從Arm CoreSight SoC-400 Technical Reference Manual章節(jié)Debug Access Port中你可以獲取到詳細的信息。這里只做關(guān)鍵內(nèi)容介紹,只介紹涉及的部分。
參考文檔:Arm CoreSight SoC-400 Technical Reference Manual , CoreSight Components Technical Reference Manual , Arm CoreSight Architecture Specification。
從Arm CoreSight SoC-400 Technical Reference Manual中Figure 4-1 Structure of the CoreSight SoC-400 DAP components看出SWD只是接入芯片調(diào)試子系統(tǒng)的一種方式而已。
那么外部調(diào)試工具是怎么通過SWD的方式接入到ARM核內(nèi)部的呢?下面我們詳細介紹一下CoreSight SoC-400 DAP組件架構(gòu)。
DAP是片外調(diào)試工具接入SOC組件的集合。訪問方式是按照ARM?調(diào)試接口架構(gòu)規(guī)范ADIv5.0~ADIv5.2實現(xiàn)。
DAP由以下組件組成:
?DP用于管理與外部調(diào)試器的連接。
?AP訪問片上系統(tǒng)資源。 每種類型的AP可以有多個。
?DAPBUS互連,用于將DP連接到一個或多個AP。
AP提供了非侵入式訪問權(quán)限:
?CoreSight組件下載或者燒錄模型。 通常是通過系統(tǒng)級CoreSight APB總線和APB-AP來完成的。
?內(nèi)存映射的系統(tǒng)組件,通常使用AXI-AP或AHB-AP。
?使用JTAG-AP的舊版JTAG配置的調(diào)試組件。
而且,某些支持CoreSight的處理器直接連接到DAPBUS互連,并實現(xiàn)自己的ADIv5兼容AP。
CoreSight SoC具有單個多功能DP,如下所示:
SWJ-DP 這是一個組合調(diào)試端口,可以通過ADIv5.1定義的JTAG或串行線協(xié)議進行通信。它包含兩個調(diào)試端口SW-DP和JTAG-DP,您可以通過接口時序命令選擇它們,以在調(diào)試端口接口之間切換。
JTAG-DP 兼容DP架構(gòu)版本0。SW-DP兼容DP架構(gòu)版本2和Serial Wire協(xié)議版本2,使SW-DP可以與其他SW-DP或其他實現(xiàn)組件共享連接。
CoreSight SoC中包含的AP端口是:
AXI-AP AXI-AP實現(xiàn)了ADIv5存儲器訪問端口(MEM-AP)架構(gòu),以直接連接到AXI存儲器系統(tǒng)。 您可以使用適當?shù)臉蚪咏M件將其連接到其他內(nèi)存系統(tǒng)。
AHB-AP AHB-AP提供了一個AHB-Lite主站,用于訪問系統(tǒng)AHB總線。 這兼容ADIv5.1中的MEM-AP并且可以執(zhí)行8到32位訪問。
APB-AP APB-AP在AMBA v3.0中提供了一個APB主設(shè)備,用于訪問調(diào)試APB總線。 這兼容具有32位固定傳輸大小的MEM-AP體系結(jié)構(gòu)。
JTAG-AP JTAG-AP提供對片上組件的JTAG訪問,用作JTAG主端口以驅(qū)動ASIC中的JTAG鏈。 這是ADIv5.1中JTAG-AP的實現(xiàn)。
DAPBUS 互連將DP連接到AP。 系統(tǒng)可能不包含某些類型的AP,或者可能包含多個相同類型的AP。
單純看CoreSight SoC-400 DAP組件架構(gòu),感覺會比較空洞,我們結(jié)合一下S32K3XX DAP架構(gòu)圖來看一下。
從芯片的DAP architecture,不難看出S32K3采用的是Arm?CoreSight?架構(gòu)。
你基本可以找到 CoreSight SoC-400和S32K3XX的DAP architecture對應(yīng)關(guān)系:
S32K3XX CoreSight SoC-400
SWJ-DAP <--> Serial Wire JTAG Debug Port (SWJ-DP)
DAPMUX <--> DAPBUS interconnect
AHB_AP <--> AHB Access Port (AHB-AP)
APB-AP <--> APB Access Port (APB-AP)
MDM_AP <--> DAPBUS exported interface
SDA_AP <--> DAPBUS exported interface
SWJ-DP由JTAG-DP和SW-DP的組成。 它選擇JTAG或SWD作為連接機制,并啟用JTAG-DP或SW-DP作為DAP的接口。
JTAG和SWD接口
JTAG接口具有四個強制引腳tck,tms,tdi和tdo,以及一個可選的復(fù)位引腳ntrst。 JTAG-DP和SW-DP還需要獨立的上電復(fù)位npotrst。
SWD接口需要兩個引腳:
?雙向swdio信號。
?時鐘swclk,可以從設(shè)備輸入或輸出。
為了使JTAG或SWD共享連接器,必須在SWJ-DP模塊外部進行連接。 特別是,tms必須是雙向引腳,以支持SWD模式下的雙向swdio引腳。
20-Pin ARM Standard JTAG Connector
(From https://www2.keil.com/coresight/coresight-connectors)
它支持用于訪問基于ARM7和ARM9的設(shè)備的JTAG接口。 對于Cortex-Mx設(shè)備,它支持串行線和JTAG接口,以訪問Cortex-Mx設(shè)備上可用的所有SWD,SWV和JTAG信號。
那么是調(diào)試工具是通過什么方式訪問Cortex-Mx內(nèi)部的呢?
參考文檔:ARM? Debug Interface Architecture Specification ADIv5.0 to ADIv5.2
DAP訪問方式是按照ARM?調(diào)試接口架構(gòu)規(guī)范ADIv5.0~ADIv5.2實現(xiàn)。
DAP****結(jié)構(gòu)框圖如下:
來自ARM? Debug Interface Architecture Specification ADIv5.0 to ADIv5.2的Figure A1-2 Structure of the DAP, showing DPv0 JTAG-DP accesses to a generic AP
ADI包括:
Debug Access Port (DAP),DAP外部物理連接 和 DAP與內(nèi)部調(diào)試資源組件的連接。
DAP包含兩個邏輯模塊,Debug Port(DP) 和 Access Port(AP)。DP來連接外部的host,AP來訪問內(nèi)部的調(diào)試組件寄存器:
? Access to the Debug Port (DP) registers. This is provided by Debug Port accesses (DPACC).
? Access to the Access Port (AP) registers. This is provided by Access Port accesses (APACC)
ADIv5標準外部接口支持一下幾種DP:
? The JTAG Debug Port (JTAG-DP)
? The Serial Wire Debug Port (SW-DP)
? The Serial Wire/JTAG Debug Port (SWJ-DP)
內(nèi)部資源接口包含:
AP (MEM-AP or JTAG-AP)
由于我選擇使用的是SW-DP的方式訪問,我這里只對SWD協(xié)議處理流程介紹。
我們下面剖析一下SW-DP報文格式(擔心翻譯有誤,就用英文原文):
Start A single start bit, with value 0b1.
APnDP A single bit, indicating whether the Debug Port or the Access Port Access register is to be accessed. This bit is 0b0 for a DPACC access, or 0b1 for an APACC access.
RnW A single bit, indicating whether the access is a read or a write. This bit is 0b0 for a write access, or 0b1 for a read access.
A[2:3] Two bits, giving the A[3:2] address field for the DP or AP register Address:
? For a DPACC access, the register being addressed depends on the A[3:2] value and, if A[3:2]==0b01, the value that is held in SELECT. DPBANKSEL. For details, see:
— DP architecture version 1 (DPv1) address map on page B2-50
— DP architecture version 2 (DPv2) address map on page B2-51.
? For an APACC access, the register being addressed depends on the A[3:2] value and the value
that is held in SELECT.{APSEL,APBANKSEL}. For details about addressing, see:
— MEM-AP Programmers’ Model on page C2-169 for accesses to a MEM-AP register
— JTAG-AP register summary on page C3-206 for accesses to a JTAG-AP register.
Note
The A[3:2] value is transmitted Least Significant Bit (LSB) first on the wire, which is why it appears as A[2:3] on the diagrams.
Parity A single parity bit for the preceding packet. See Parity on page B4-108.
Stop A single stop bit. In the synchronous SWD protocol, this bit is always 0b0.
Park A single bit. The host must drive the Park bit HIGH to park the line before tristating it for the turnaround period, to ensure that the line is read as HIGH by the target, which is required because the pull-up on the SWD interface is weak. The target reads this bit as 0b1.
Trn Turnaround. See Line turnaround on page B4-107.
Note
All the examples that are given in this chapter show the default turnaround period of one cycle.
ACK[0:2] A three-bit target-to-host response.
WDATA[0:31]
32 bits of write data, from host to target.
RDATA[0:31]
32 bits of read data, from target to host
我們已經(jīng)了解了報文格式,那我們結(jié)合SWD的報文讀寫時序圖來理解。
Successful write operation (OK response)
Successful read operation (OK response)
下面我結(jié)合Successful write operation (OK response)和Structure of the Debug Access Port舉個例子演示一下SWD如何控制DAP。
Every AP or DP access transaction from the debugger includes two address bits, A[3:2]:
? For a DP register access, the address bits A[3:2] and SELECT.DPBANKSEL determine which register is accessed. SELECT is a DP register.
? For an AP register access, SELECT.APSEL selects an AP to access, and the address bits A[3:2] are combined with SELECT.APBANKSEL to determine which AP register is accessed, as summarized in Structure of the Debug Access Port. That is, the two address bits A[3:2] are decoded to select one of the four 32-bit words from the register bank indicated by SELECT.APBANKSEL in the AP indicated by SELECT.APSEL. Bits [1:0] of all AP and DP register addresses are 0b00.
假如我要向AP0寄存器0x14里寫入0xF0000001,調(diào)試工具操作流程如下:
使用DP寄存器向DP的SELECT寄存器寫入:
— SELECT.APSEL to 0x00. APSEL, bits[31:24]
— SELECT.APBANKSEL to 0x1. APBANKSEL, bits[7:4]
APnDP 寫0 表示DP操作,A[3:2]寫0x02表示操作0x08 SELECT 寄存器。寫入0x00000001.
使用AP寄存器向AP0的0x14寄存器寫入:
APnDP 寫1 表示AP操作,由于已經(jīng)向DP的SELECT選擇了APBANKSEL為0x01,A[3:2]寫0x01表示操作0x14 寄存器。寫入0xF0000001. 在這個情況下能訪問0x10~0x1C等4個寄存器。
其他AP操作其實流程一樣。
下面我稍微了解一下MEM-AP.
來自 ARM? Debug Interface Architecture Specification ADIv5.0 to ADIv5.2的Figure C2-1 MEM-AP connecting the DP to debug components
說白了MEM-AP為DAP提供了一種直接訪問系統(tǒng)地址空間的訪問。操作其實和通用AP的訪問一樣。MEM-AP訪問系統(tǒng)地址空間,其實有多了一層間接訪問。我們直接從OpenOCD源碼中看整個過程。
https://sourceforge.net/p/openocd/code/ci/v0.11.0/tree/src/target/cortex_m.c#l2521
struct target_type cortexm_target = {
.name = "cortex_m",
.deprecated_name = "cortex_m3",
.poll = cortex_m_poll,
.arch_state = armv7m_arch_state,
.target_request_data = cortex_m_target_request_data,
.halt = cortex_m_halt,
.resume = cortex_m_resume,
.step = cortex_m_step,
.assert_reset = cortex_m_assert_reset,
.deassert_reset = cortex_m_deassert_reset,
.soft_reset_halt = cortex_m_soft_reset_halt,
.get_gdb_arch = arm_get_gdb_arch,
.get_gdb_reg_list = armv7m_get_gdb_reg_list,
.read_memory = cortex_m_read_memory,
.write_memory = cortex_m_write_memory,
.checksum_memory = armv7m_checksum_memory,
.blank_check_memory = armv7m_blank_check_memory,
.run_algorithm = armv7m_run_algorithm,
.start_algorithm = armv7m_start_algorithm,
.wait_algorithm = armv7m_wait_algorithm,
.add_breakpoint = cortex_m_add_breakpoint,
.remove_breakpoint = cortex_m_remove_breakpoint,
.add_watchpoint = cortex_m_add_watchpoint,
.remove_watchpoint = cortex_m_remove_watchpoint,
.commands = cortex_m_command_handlers,
.target_create = cortex_m_target_create,
.target_jim_configure = adiv5_jim_configure,
.init_target = cortex_m_init_target,
.examine = cortex_m_examine,
.deinit_target = cortex_m_deinit_target,
.profiling = cortex_m_profiling,
};
OpenOCD通過DAP MEM AP的訪問實現(xiàn)Cortex M系統(tǒng)地址空間的訪問。
cortex_m_read_memory和cortex_m_write_memory實現(xiàn)了讀寫操作。看一下這兩個函數(shù)是怎么實現(xiàn)的。
cortex_m_read_memory實現(xiàn)
static int cortex_m_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
struct armv7m_common *armv7m = target_to_armv7m(target);
if (armv7m->arm.is_armv6m) {
/* armv6m does not handle unaligned memory access */
if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
return ERROR_TARGET_UNALIGNED_ACCESS;
}
return mem_ap_read_buf(armv7m->debug_ap, buffer, size, count, address);
}
int mem_ap_read_buf(struct adiv5_ap *ap,
uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address)
{
return mem_ap_read(ap, buffer, size, count, address, true);
}
/**
* Synchronous read of a block of memory, using a specific access size.
*
* @param ap The MEM-AP to access.
* @param buffer The data buffer to receive the data. No particular alignment is assumed.
* @param size Which access size to use, in bytes. 1, 2 or 4.
* @param count The number of reads to do (in size units, not bytes).
* @param adr Address to be read; it must be readable by the currently selected MEM-AP.
* @param addrinc Whether the target address should be increased after each read or not. This
* should normally be true, except when reading from e.g. a FIFO.
* @return ERROR_OK on success, otherwise an error code.
*/
static int mem_ap_read(struct adiv5_ap *ap, uint8_t *buffer, uint32_t size, uint32_t count,
uint32_t adr, bool addrinc)
{
struct adiv5_dap *dap = ap->dap;
size_t nbytes = size * count;
const uint32_t csw_addrincr = addrinc ? CSW_ADDRINC_SINGLE : CSW_ADDRINC_OFF;
uint32_t csw_size;
uint32_t address = adr;
int retval = ERROR_OK;
/* TI BE-32 Quirks mode:
* Reads on big-endian TMS570 behave strangely differently than writes.
* They read from the physical address requested, but with DRW byte-reversed.
* For example, a byte read from address 0 will place the result in the high bytes of DRW.
* Also, packed 8-bit and 16-bit transfers seem to sometimes return garbage in some bytes,
* so avoid them. */
if (size == 4)
csw_size = CSW_32BIT;
else if (size == 2)
csw_size = CSW_16BIT;
else if (size == 1)
csw_size = CSW_8BIT;
else
return ERROR_TARGET_UNALIGNED_ACCESS;
if (ap->unaligned_access_bad && (adr % size != 0))
return ERROR_TARGET_UNALIGNED_ACCESS;
/* Allocate buffer to hold the sequence of DRW reads that will be made. This is a significant
* over-allocation if packed transfers are going to be used, but determining the real need at
* this point would be messy. */
uint32_t *read_buf = calloc(count, sizeof(uint32_t));
/* Multiplication count * sizeof(uint32_t) may overflow, calloc() is safe */
uint32_t *read_ptr = read_buf;
if (read_buf == NULL) {
LOG_ERROR("Failed to allocate read buffer");
return ERROR_FAIL;
}
/* Queue up all reads. Each read will store the entire DRW word in the read buffer. How many
* useful bytes it contains, and their location in the word, depends on the type of transfer
* and alignment. */
while (nbytes > 0) {
uint32_t this_size = size;
/* Select packed transfer if possible */
if (addrinc && ap->packed_transfers && nbytes >= 4
&& max_tar_block_size(ap->tar_autoincr_block, address) >= 4) {
this_size = 4;
retval = mem_ap_setup_csw(ap, csw_size | CSW_ADDRINC_PACKED);
} else {
retval = mem_ap_setup_csw(ap, csw_size | csw_addrincr);
}
if (retval != ERROR_OK)
break;
retval = mem_ap_setup_tar(ap, address);
if (retval != ERROR_OK)
break;
retval = dap_queue_ap_read(ap, MEM_AP_REG_DRW, read_ptr++);
if (retval != ERROR_OK)
break;
nbytes -= this_size;
if (addrinc)
address += this_size;
mem_ap_update_tar_cache(ap);
}
if (retval == ERROR_OK)
retval = dap_run(dap);
/* Restore state */
address = adr;
nbytes = size * count;
read_ptr = read_buf;
/* If something failed, read TAR to find out how much data was successfully read, so we can
* at least give the caller what we have. */
if (retval != ERROR_OK) {
uint32_t tar;
if (mem_ap_read_tar(ap, &tar) == ERROR_OK) {
/* TAR is incremented after failed transfer on some devices (eg Cortex-M4) */
LOG_ERROR("Failed to read memory at 0x%08"PRIx32, tar);
if (nbytes > tar - address)
nbytes = tar - address;
} else {
LOG_ERROR("Failed to read memory and, additionally, failed to find out where");
nbytes = 0;
}
}
/* Replay loop to populate caller's buffer from the correct word and byte lane */
while (nbytes > 0) {
uint32_t this_size = size;
if (addrinc && ap->packed_transfers && nbytes >= 4
&& max_tar_block_size(ap->tar_autoincr_block, address) >= 4) {
this_size = 4;
}
if (dap->ti_be_32_quirks) {
switch (this_size) {
case 4:
*buffer++ = *read_ptr >> 8 * (3 - (address++ & 3));
*buffer++ = *read_ptr >> 8 * (3 - (address++ & 3));
/* fallthrough */
case 2:
*buffer++ = *read_ptr >> 8 * (3 - (address++ & 3));
/* fallthrough */
case 1:
*buffer++ = *read_ptr >> 8 * (3 - (address++ & 3));
}
} else {
switch (this_size) {
case 4:
*buffer++ = *read_ptr >> 8 * (address++ & 3);
*buffer++ = *read_ptr >> 8 * (address++ & 3);
/* fallthrough */
case 2:
*buffer++ = *read_ptr >> 8 * (address++ & 3);
/* fallthrough */
case 1:
*buffer++ = *read_ptr >> 8 * (address++ & 3);
}
}
read_ptr++;
nbytes -= this_size;
}
free(read_buf);
return retval;
}
static int cortex_m_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
struct armv7m_common *armv7m = target_to_armv7m(target);
if (armv7m->arm.is_armv6m) {
/* armv6m does not handle unaligned memory access */
if (((size == 4) && (address & 0x3u)) || ((size == 2) && (address & 0x1u)))
return ERROR_TARGET_UNALIGNED_ACCESS;
}
return mem_ap_write_buf(armv7m->debug_ap, buffer, size, count, address);
}
int mem_ap_write_buf(struct adiv5_ap *ap,
const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address)
{
return mem_ap_write(ap, buffer, size, count, address, true);
}
/**
* Synchronous write of a block of memory, using a specific access size.
*
* @param ap The MEM-AP to access.
* @param buffer The data buffer to write. No particular alignment is assumed.
* @param size Which access size to use, in bytes. 1, 2 or 4.
* @param count The number of writes to do (in size units, not bytes).
* @param address Address to be written; it must be writable by the currently selected MEM-AP.
* @param addrinc Whether the target address should be increased for each write or not. This
* should normally be true, except when writing to e.g. a FIFO.
* @return ERROR_OK on success, otherwise an error code.
*/
static int mem_ap_write(struct adiv5_ap *ap, const uint8_t *buffer, uint32_t size, uint32_t count,
uint32_t address, bool addrinc)
{
struct adiv5_dap *dap = ap->dap;
size_t nbytes = size * count;
const uint32_t csw_addrincr = addrinc ? CSW_ADDRINC_SINGLE : CSW_ADDRINC_OFF;
uint32_t csw_size;
uint32_t addr_xor;
int retval = ERROR_OK;
/* TI BE-32 Quirks mode:
* Writes on big-endian TMS570 behave very strangely. Observed behavior:
* size write address bytes written in order
* 4 TAR ^ 0 (val >> 24), (val >> 16), (val >> 8), (val)
* 2 TAR ^ 2 (val >> 8), (val)
* 1 TAR ^ 3 (val)
* For example, if you attempt to write a single byte to address 0, the processor
* will actually write a byte to address 3.
*
* To make writes of size < 4 work as expected, we xor a value with the address before
* setting the TAP, and we set the TAP after every transfer rather then relying on
* address increment. */
if (size == 4) {
csw_size = CSW_32BIT;
addr_xor = 0;
} else if (size == 2) {
csw_size = CSW_16BIT;
addr_xor = dap->ti_be_32_quirks ? 2 : 0;
} else if (size == 1) {
csw_size = CSW_8BIT;
addr_xor = dap->ti_be_32_quirks ? 3 : 0;
} else {
return ERROR_TARGET_UNALIGNED_ACCESS;
}
if (ap->unaligned_access_bad && (address % size != 0))
return ERROR_TARGET_UNALIGNED_ACCESS;
while (nbytes > 0) {
uint32_t this_size = size;
/* Select packed transfer if possible */
if (addrinc && ap->packed_transfers && nbytes >= 4
&& max_tar_block_size(ap->tar_autoincr_block, address) >= 4) {
this_size = 4;
retval = mem_ap_setup_csw(ap, csw_size | CSW_ADDRINC_PACKED);
} else {
retval = mem_ap_setup_csw(ap, csw_size | csw_addrincr);
}
if (retval != ERROR_OK)
break;
retval = mem_ap_setup_tar(ap, address ^ addr_xor);
if (retval != ERROR_OK)
return retval;
/* How many source bytes each transfer will consume, and their location in the DRW,
* depends on the type of transfer and alignment. See ARM document IHI0031C. */
uint32_t outvalue = 0;
uint32_t drw_byte_idx = address;
if (dap->ti_be_32_quirks) {
switch (this_size) {
case 4:
outvalue |= (uint32_t)*buffer++ << 8 * (3 ^ (drw_byte_idx++ & 3) ^ addr_xor);
outvalue |= (uint32_t)*buffer++ << 8 * (3 ^ (drw_byte_idx++ & 3) ^ addr_xor);
outvalue |= (uint32_t)*buffer++ << 8 * (3 ^ (drw_byte_idx++ & 3) ^ addr_xor);
outvalue |= (uint32_t)*buffer++ << 8 * (3 ^ (drw_byte_idx & 3) ^ addr_xor);
break;
case 2:
outvalue |= (uint32_t)*buffer++ << 8 * (1 ^ (drw_byte_idx++ & 3) ^ addr_xor);
outvalue |= (uint32_t)*buffer++ << 8 * (1 ^ (drw_byte_idx & 3) ^ addr_xor);
break;
case 1:
outvalue |= (uint32_t)*buffer++ << 8 * (0 ^ (drw_byte_idx & 3) ^ addr_xor);
break;
}
} else {
switch (this_size) {
case 4:
outvalue |= (uint32_t)*buffer++ << 8 * (drw_byte_idx++ & 3);
outvalue |= (uint32_t)*buffer++ << 8 * (drw_byte_idx++ & 3);
/* fallthrough */
case 2:
outvalue |= (uint32_t)*buffer++ << 8 * (drw_byte_idx++ & 3);
/* fallthrough */
case 1:
outvalue |= (uint32_t)*buffer++ << 8 * (drw_byte_idx & 3);
}
}
nbytes -= this_size;
retval = dap_queue_ap_write(ap, MEM_AP_REG_DRW, outvalue);
if (retval != ERROR_OK)
break;
mem_ap_update_tar_cache(ap);
if (addrinc)
address += this_size;
}
/* REVISIT: Might want to have a queued version of this function that does not run. */
if (retval == ERROR_OK)
retval = dap_run(dap);
if (retval != ERROR_OK) {
uint32_t tar;
if (mem_ap_read_tar(ap, &tar) == ERROR_OK)
LOG_ERROR("Failed to write memory at 0x%08"PRIx32, tar);
else
LOG_ERROR("Failed to write memory and, additionally, failed to find out where");
}
return retval;
}
MEM-AP這里我就不畫圖介紹了,對于我來說只要知道MEM-AP是如何幫助DAP訪問到系統(tǒng)地址空間的原理就可以了,大家直接看源碼實現(xiàn)去理解。大家想要知道更多細節(jié)可以參考ADIv5.0~ADIv5.2章節(jié) 7. The Memory Access Port (MEM-AP)
審核編輯:黃飛
-
ARM
+關(guān)注
關(guān)注
134文章
9105瀏覽量
367923 -
調(diào)試器
+關(guān)注
關(guān)注
1文章
305瀏覽量
23763 -
車載芯片
+關(guān)注
關(guān)注
0文章
75瀏覽量
14751 -
SWD
+關(guān)注
關(guān)注
1文章
57瀏覽量
11862 -
調(diào)試接口
+關(guān)注
關(guān)注
0文章
10瀏覽量
5110
發(fā)布評論請先 登錄
相關(guān)推薦
評論