SWD下载调试接口原理深度剖析

这篇具有很好参考价值的文章主要介绍了SWD下载调试接口原理深度剖析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

由于我们公司自己需要开发烧录工具,本人通过google搜相关文档和看ARM公司的技术文档,终于实现了这个功能。该篇幅敢很自信的说把SWD理论讲的最浅显易懂的。

作为ARM嵌入式工程师,下载调试器都应该知道,但你真正了解SWD如何下载调试的么?ARM 芯片通过什么物理接口和什么方式连接完全取决于芯片Debug子系统的架构如何?

你可以从芯片手册的Debug章节获得DAP(Debug Access Port)的信息。我调试的是NXP公司最新车载芯片S32K3系列的芯片,从S32K3xx Reference Manual中Debug 子系统章节中得知该芯片的debug和Trace接口基于Arm CoreSight SoC-400的标准。 从Arm CoreSight SoC-400 Technical Reference Manual章节Debug Access Port中你可以获取到详细的信息。这里只做关键内容介绍,只介绍涉及的部分。

参考文档: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只是接入芯片调试子系统的一种方式而已。

SWD下载调试接口原理深度剖析

那么外部调试工具是怎么通过SWD的方式接入到ARM核内部的呢?下面我们详细介绍一下CoreSight SoC-400 DAP组件架构。

DAP是片外调试工具接入SOC组件的集合。访问方式是按照ARM®调试接口架构规范ADIv5.0~ADIv5.2实现。
DAP由以下组件组成:
DP用于管理与外部调试器的连接。
AP访问片上系统资源。 每种类型的AP可以有多个。
DAPBUS互连,用于将DP连接到一个或多个AP。
AP提供了非侵入式访问权限:
CoreSight组件下载或者烧录模型。 通常是通过系统级CoreSight APB总线和APB-AP来完成的。
•内存映射的系统组件,通常使用AXI-APAHB-AP
•使用JTAG-AP的旧版JTAG配置的调试组件。
而且,某些支持CoreSight的处理器直接连接到DAPBUS互连,并实现自己的ADIv5兼容AP。

CoreSight SoC具有单个多功能DP,如下所示:

SWJ-DP  这是一个组合调试端口,可以通过ADIv5.1定义的JTAG或串行线协议进行通信。它包含两个调试端口SW-DPJTAG-DP,您可以通过接口时序命令选择它们,以在调试端口接口之间切换。

JTAG-DP 兼容DP架构版本0。SW-DP兼容DP架构版本2和Serial Wire协议版本2,使SW-DP可以与其他SW-DP或其他实现组件共享连接。

CoreSight SoC中包含的AP端口是:

AXI-AP AXI-AP实现了ADIv5存储器访问端口(MEM-AP)架构,以直接连接到AXI存储器系统。 您可以使用适当的桥接组件将其连接到其他内存系统。

AHB-AP AHB-AP提供了一个AHB-Lite主站,用于访问系统AHB总线。 这兼容ADIv5.1中的MEM-AP并且可以执行8到32位访问。

APB-AP APB-AP在AMBA v3.0中提供了一个APB主设备,用于访问调试APB总线。 这兼容具有32位固定传输大小的MEM-AP体系结构。

JTAG-AP JTAG-AP提供对片上组件的JTAG访问,用作JTAG主端口以驱动ASIC中的JTAG链。 这是ADIv5.1中JTAG-AP的实现。

DAPBUS 互连将DP连接到AP。 系统可能不包含某些类型的AP,或者可能包含多个相同类型的AP。

单纯看CoreSight SoC-400 DAP组件架构,感觉会比较空洞,我们结合一下S32K3XX DAP架构图来看一下。

SWD下载调试接口原理深度剖析

从芯片的DAP architecture,不难看出S32K3采用的是Arm®CoreSight™架构。
你基本可以找到 CoreSight SoC-400和S32K3XX的DAP architecture对应关系:
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,以及一个可选的复位引脚ntrst。 JTAG-DP和SW-DP还需要独立的上电复位npotrst。
SWD接口需要两个引脚:
•双向swdio信号。
•时钟swclk,可以从设备输入或输出。

为了使JTAG或SWD共享连接器,必须在SWJ-DP模块外部进行连接。 特别是,tms必须是双向引脚,以支持SWD模式下的双向swdio引脚。
20-Pin ARM Standard JTAG Connector

SWD下载调试接口原理深度剖析

(From https://www2.keil.com/coresight/coresight-connectors)

它支持用于访问基于ARM7和ARM9的设备的JTAG接口。 对于Cortex-Mx设备,它支持串行线和JTAG接口,以访问Cortex-Mx设备上可用的所有SWD,SWV和JTAG信号。

那么是调试工具是通过什么方式访问Cortex-Mx内部的呢?

参考文档:ARM® Debug Interface Architecture Specification ADIv5.0 to ADIv5.2

DAP访问方式是按照ARM®调试接口架构规范ADIv5.0~ADIv5.2实现。

DAP****结构框图如下:

来自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

SWD下载调试接口原理深度剖析

ADI包括:
Debug Access Port (DAP),DAP外部物理连接 和 DAP与内部调试资源组件的连接。

DAP包含两个逻辑模块,Debug Port(DP) 和 Access Port(AP)。DP来连接外部的host,AP来访问内部的调试组件寄存器:
• 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)

内部资源接口包含:
AP (MEM-AP or JTAG-AP)

由于我选择使用的是SW-DP的方式访问,我这里只对SWD协议处理流程介绍。
我们下面剖析一下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

SWD下载调试接口原理深度剖析

— DP architecture version 2 (DPv2) address map on page B2-51.

SWD下载调试接口原理深度剖析

• 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

SWD下载调试接口原理深度剖析

— JTAG-AP register summary on page C3-206 for accesses to a JTAG-AP register.

SWD下载调试接口原理深度剖析


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.

SWD下载调试接口原理深度剖析

WDATA[0:31]
32 bits of write data, from host to target.

RDATA[0:31]
32 bits of read data, from target to host

我们已经了解了报文格式,那我们结合SWD的报文读写时序图来理解。

Successful write operation (OK response)

SWD下载调试接口原理深度剖析

Successful read operation (OK response)

SWD下载调试接口原理深度剖析

下面我结合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,调试工具操作流程如下:

SWD下载调试接口原理深度剖析

使用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操作,由于已经向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

SWD下载调试接口原理深度剖析

说白了MEM-AP为DAP提供了一种直接访问系统地址空间的访问。操作其实和通用AP的访问一样。MEM-AP访问系统地址空间,其实有多了一层间接访问。我们直接从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的访问实现Cortex M系统地址空间的访问。

cortex_m_read_memory和cortex_m_write_memory实现了读写操作。看一下这两个函数是怎么实现的。

cortex_m_read_memory实现

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访问到系统地址空间的原理就可以了,大家直接看源码实现去理解。大家想要知道更多细节可以参考ADIv5.0~ADIv5.2章节 7. The Memory Access Port (MEM-AP)

参考文档:
S32K3xx Reference Manual
Arm CoreSight SoC-400 Technical Reference Manual
CoreSight Components Technical Reference Manual
Arm CoreSight Architecture Specification
ARM® Debug Interface Architecture Specification ADIv5.0 to ADIv5.2
https://www2.keil.com/coresight/coresight-connectors
Programming internal SRAM over SWD 文章来源地址https://www.toymoban.com/news/detail-423272.html

到了这里,关于SWD下载调试接口原理深度剖析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深度剖析Redis九种数据结构实现原理,建议收藏

    Redis 是一个高性能的键值存储系统,支持多种数据结构。 包含五种基本类型 String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),和三种特殊类型 Geo(地理位置)、HyperLogLog(基数统计)、Bitmaps(位图)。 每种数据结构都是为了解决特定问题而设计

    2023年04月11日
    浏览(41)
  • 使用Beego和MySQL实现帖子和评论的应用,并进行接口测试(附源码和代码深度剖析)

    经过对需求的分析,我增加了一些额外的东西,比如增加了 user 用户,因为我考虑到帖子或者是评论(跟回帖差不多)都会有作者,主要的功能有增加帖子、查看所有或单个帖子、增加评论、查看某个帖子的所有评论或单个评论。数据我是将它存在数据库中,而不是内存中。

    2024年02月14日
    浏览(43)
  • jlink 与 swd 接口定义

    J-Link是SEGGER公司为支持仿真ARM内核推出的JTAG仿真器。J-Link 支持所有基于ARM架构的处理器或微控制器配合IAR EWAR,ADS,KEIL等集成开发环境进行开发过程中进行单步控制执行调试。 J-Link除了可以配合集成开发环境进行调试程序,进行程序下载之外,J-Link还可以单独使用。比如在

    2023年04月22日
    浏览(39)
  • 【C++进阶(六)】STL大法--栈和队列深度剖析&优先级队列&适配器原理

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++从入门到精通⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习C++   🔝🔝 和C语言学习期间的学习顺序一样 顺序表,链表过了就是栈和队列 但是栈和队列非常特殊,它的内部结构 并不是靠自己实现的,而是一种 适配

    2024年02月08日
    浏览(43)
  • 深度解析:使用Postman调试微信支付接口的完美指南

    在使用 Postman 调试微信支付接口之前,你需要做好以下准备: 安装 Postman 客户端应用,或使用网页版; 成为 微信支付商户; 已申请 商户API私钥。 当你已经具备这三个条件,就可以进入微信支付接口调试之旅了~ 方式一:通过 fork 方式 为了帮助商户开发者快速上手,微信官

    2024年02月08日
    浏览(40)
  • Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(二)—漏洞原理

    2.15.0之前版漏洞相关文章 Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(一)—开篇与基础知识 Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(二)—漏洞原理 Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(三)—复现步骤(攻击方法) Log4j2注入漏洞(CVE-2021-44228)万字深度剖析(四)—漏洞修复原理 2.15.

    2024年02月07日
    浏览(78)
  • 由于找不到msvcp140.dll文件,我们要怎么解决这种情况?

    在使用电脑的过程中,我们经常会遇到各种各样的问题,其中之一就是缺少msvcp140.dll文件。这个问题通常会导致某些软件无法正常运行,而且很多人对于如何解决这个问题并不是很清楚。本文将会介绍多种修复方法,并对比哪种方法比较方便。 一.什么是msvcp140.dll msvcp140.dll是

    2024年02月10日
    浏览(58)
  • 自制ST-Link V2.1教程(SWD调试+虚拟串口+虚拟U盘)

    ST-LINK 是ST公司开发的一款专门用于 STM8 、 STM32 单片机调试的硬件设备,截至到现在官方已经推出三代产品: V1 、 V2 、 V3 。 ST-LINK V1 是比较老的版本,官网上显示已经停产。目前市面上很少看见有V1版,基本被V2版取代了。 ST-LINK V2 是目前比较主流的版本,第一款V2产品诞生

    2023年04月08日
    浏览(52)
  • 【从JVM看Java,三问继承和多态,是什么?为什么?怎么做?深度剖析JVM的工作原理】

    《计算机底层原理专栏》:欢迎大家订阅学习,能够帮助到各位就是对我最大的鼓励! 文章目录 系列文章目录 前言 一、JVM是什么 二、 什么是继承 三、 什么是多态 总结         这篇文章聚焦JVM的实现原理,我更专注于从一个语言的底层原理,去剖析他的语法所实现的意义

    2024年02月05日
    浏览(55)
  • STM32 HAL SWD下载与串口通信

    SWD是ST公司推出的开源的四线下载方式,分别为3V3、SWD、SWCLK、GND,相比JTAG等可以用较少的线来实现下载和仿真。 首先你需要购买一个DAPLINK,tb购买15块钱左右。只需要接到STM32F103C8T6最小系统板上面的同样的这四个排针即完成接线。 而对于类似正点原子的精英板等,则需要在

    2024年02月03日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包