FTDI JTAG 数据传输格式与 SCAN 命令完整规范 📋 文档概述
驱动文件:src/jtag/drivers/openjtag.c 适配变体:Standard FTDI (libftdi) 核心目的:定义 ftdi_write_data()/ftdi_read_data() 数据格式,规范 SCAN 命令在 IR/DR 切换中的完整使用流程 文档版本:1.2(2025-01-22) 目录 整体数据流 核心命令格式 SCAN 命令完整使用流程 数据传输与解析 调试与问题排查 核心要点速查 1. 整体数据流 1.1 数据流向架构
jtag_build_buffer() 构建原始数据
jtag_read_buffer() 解析接收数据
openjtag_add_scan() 编码为 OpenJTAG 协议
openjtag_execute_tap_queue() 解析数据
OpenOCD 应用层 (dmi_write/dmi_read/寄存器操作)
1.2 缓冲区基础格式 缓冲区类型 格式 核心特点 TX 缓冲区 [命令1][数据1][命令2][数据2]...命令+数据成对出现(单字节命令除外) RX 缓冲区 [数据1][数据2]...[数据N]仅包含数据,无命令字节
2. 核心命令格式 2.1 命令字节总览 命令类型 命令码 功能描述 数据字节 SET_STATE 0x01 设置 JTAG TAP 状态机 1 SCAN 0x06 传输 IR/DR 比特流(核心) 1 SPEED 0x00 设置 JTAG 通信速率 1 RESET (TRST) 0x03 硬件复位 TAP 状态机 0 RESET (SRST) 0x54 系统复位控制 0 RUNTEST 0x07 设置空闲周期数 1 MSB 模式 0x75 启用高位优先传输 0
2.2 SCAN 命令(0x06)- 核心指令 2.2.1 命令格式(8位) 位 7-5 位 4 位 3-0 说明 位数编码 TMS 0x06 固定格式,TMS控制状态退出
2.2.2 位数编码映射 编码值 有效位数 应用场景 示例命令 0 1 最后1位(收尾) 0x16 1 2 最后2位(收尾) 0x26 2 3 最后3位(收尾) 0x96 3 4 最后4位(收尾) 0x36 4 5 最后5位(收尾) 0x56 5 6 最后6位(收尾) 0x56 6 7 最后7位(收尾) 0x66 7 8 中间8位(继续传输) 0xE6
2.2.3 TMS 位规则 0:中间块,保持 SHIFT 状态,继续传输1:最后块,退出 SHIFT 状态,切换到 EXIT12.2.4 典型示例 场景 二进制 十六进制 说明 DR 中间块(8位) 1110 0110 0xE6 TMS=0,继续传输 IR 最后5位 0101 0110 0x56 TMS=1,5位 IR 收尾 DR 最后1位 0001 0110 0x16 TMS=1,41位 DR 最后1位
2.3 SET_STATE 命令(0x01) 2.3.1 命令格式 位 7-4 位 3-0 说明 状态码 0x01 控制 TAP 状态机切换
2.3.2 核心状态码映射 状态名称 状态码 命令字节 用途 TAP_RESET 0 0x01 复位状态机 TAP_IRSHIFT 11 0xB1 进入 IR 移位模式 TAP_IRUPDATE 15 0xF1 使 IR 指令生效 TAP_DRSHIFT 4 0x41 进入 DR 移位模式 TAP_DRUPDATE 8 0x81 使 DR 数据生效/触发读取
2.4 SPEED 命令(0x00) 速度编码 频率 (kHz) 命令字节 0 48000 0x00 2 24000 0x20 4 12000 0x40 6 6000 0x60 8 3000 0x80 10 1500 0xA0 12 750 0xC0 14 375 0xE0
3. SCAN 命令完整使用流程 3.1 通用准备阶段(必选) 步骤 操作 TX 缓冲区数据 说明 1 复位 TAP 状态机 0x01 0x00 初始化状态机 2 设置通信速率 0x00 0x00 48MHz(可按需调整) 3 启用 MSB 模式 0x75 高位优先传输
3.2 IR 切换 + SCAN 传输流程(定义操作类型) 目标:写入 DMI 指令(5位 IR,值为 0x04) 阶段 步骤 操作 TX 缓冲区数据 关键说明 状态准备 4 切换到 IRSELECT 0x91 0x00 进入 IR 选择阶段 5 切换到 IRCAPTURE 0xA1 0x00 准备捕获 IR 数据 6 切换到 IRSHIFT 0xB1 0x00 核心:进入 IR 移位模式 SCAN 传输 7 发送 IR 数据 0x56 0x04 0x56=最后5位+TMS=1,0x04=DMI指令 状态收尾 8 切换到 IREXIT1 0xC1 0x00 退出 IR 移位模式 9 切换到 IRPAUSE 0xD1 0x00 暂存 IR 数据 10 切换到 IRUPDATE 0xF1 0x00 核心:使 IR 指令生效 验证(可选) 11 读取 IR 返回值 RX[0] = 0x04 右移对齐:0x04 >> (8-5) = 0x04
3.3 DR 切换 + SCAN 传输流程(执行具体操作) 目标:写入 DMI 寄存器(地址0x10,数据0x12345678,41位 DR) 阶段 步骤 操作 TX 缓冲区数据 关键说明 状态准备 12 切换到 DRSELECT 0x21 0x00 进入 DR 选择阶段 13 切换到 DRCAPTURE 0x31 0x00 准备捕获 DR 数据 14 切换到 DRSHIFT 0x41 0x00 核心:进入 DR 移位模式 SCAN 传输 15 DR 中间块1 0xE6 0x10 地址[7:0] = 0x10 16 DR 中间块2 0xE6 0x00 地址[15:8] = 0x00 17 DR 中间块3 0xE6 0x00 地址[23:16] = 0x00 18 DR 中间块4 0xE6 0x21 op=2(写)+地址[28:24] 19 DR 中间块5 0xE6 0x78 数据[7:0] = 0x78 20 DR 中间块6 0xE6 0x56 数据[15:8] = 0x56 21 DR 中间块7 0xE6 0x34 数据[23:16] = 0x34 22 DR 中间块8 0xE6 0x12 数据[31:24] = 0x12 23 DR 最后块 0x16 0x00 0x16=最后1位+TMS=1,exec=0 状态收尾 24 切换到 DREXIT1 0x51 0x00 退出 DR 移位模式 25 切换到 DRPAUSE 0x61 0x00 暂存 DR 数据 26 切换到 DRUPDATE 0x81 0x00 核心:完成寄存器写入
3.4 完整 TX 缓冲区示例(IR+DR 组合操作) // 通用准备 0x01 0x00 // TAP_RESET 0x00 0x00 // SPEED=48MHz 0x75 // MSB 模式 // IR 切换 0x91 0x00 // IRSELECT 0xA1 0x00 // IRCAPTURE 0xB1 0x00 // IRSHIFT 0x56 0x04 // SCAN: IR最后5位 + DMI指令 0xC1 0x00 // IREXIT1 0xD1 0x00 // IRPAUSE 0xF1 0x00 // IRUPDATE // DR 切换 0x21 0x00 // DRSELECT 0x31 0x00 // DRCAPTURE 0x41 0x00 // DRSHIFT 0xE6 0x10 // SCAN: DR中间块1 0xE6 0x00 // SCAN: DR中间块2 0xE6 0x00 // SCAN: DR中间块3 0xE6 0x21 // SCAN: DR中间块4 0xE6 0x78 // SCAN: DR中间块5 0xE6 0x56 // SCAN: DR中间块6 0xE6 0x34 // SCAN: DR中间块7 0xE6 0x12 // SCAN: DR中间块8 0x16 0x00 // SCAN: DR最后1位 0x51 0x00 // DREXIT1 0x61 0x00 // DRPAUSE 0x81 0x00 // DRUPDATE4. 数据传输与解析 4.1 发送数据(TX 缓冲区) 4.1.1 核心规则 命令与数据严格成对(单字节命令除外) SCAN 命令分块规则:数据长度 >8 位:n个中间块(0xE6) + 1个最后块(按剩余位数编码) 数据长度 ≤8 位:直接使用最后块编码 所有数据均为 MSB 优先传输 4.2 接收数据(RX 缓冲区) 4.2.1 解析流程核心代码 static int openjtag_execute_tap_queue ( void ) { // 1. 发送 TX 数据 openjtag_buf_write ( usb_tx_buf, usb_tx_buf_offs, & written) ; // 2. 接收 RX 数据 openjtag_buf_read ( usb_rx_buf, usb_tx_buf_offs, & usb_rx_buf_len) ; // 3. 解析 SCAN 数据(核心:右移对齐) while ( res_count< openjtag_scan_result_count) { len= openjtag_scan_result_buffer[ res_count] . bits; buffer= openjtag_scan_result_buffer[ res_count] . buffer; while ( len> 0 ) { if ( len<= 8 ) { // <8位数据:右移对齐(MSB→LSB) buffer[ count] = usb_rx_buf[ rx_offs] >> ( 8 - len) ; len= 0 ; } else { // ≥8位数据:直接读取 buffer[ count] = usb_rx_buf[ rx_offs] ; len-= 8 ; } rx_offs++ ; count++ ; } res_count++ ; } return ERROR_OK; } 4.2.2 右移对齐示例 有效位数 接收字节 右移量 计算过程 最终结果 3 0x1A 5 0x1A >> 5 = 0x03 0x03 5 0xD6 3 0xD6 >> 3 = 0x1A 0x1A 1 0x80 7 0x80 >> 7 = 0x01 0x01
4.3 典型 RX 缓冲区示例(DMI 读取) 场景:读取地址0x10,返回数据0xDEADBEEF RX 偏移 内容 说明 0x00 0x21 status=0 + op=1(读操作) 0x01 0xEF data[7:0] = 0xEF 0x02 0xBE data[15:8] = 0xBE 0x03 0xAD data[23:16] = 0xAD 0x04 0xDE data[31:24] = 0xDE 0x05 0x00 exec=0(最后1位,右移后=0)
解析结果 :0xDEADBEEF
5. 调试与问题排查 5.1 调试技巧 5.1.1 启用 USB 通信日志 # define _DEBUG_USB_COMMS_ // 输出示例: // USB WRITE: 0xB1 0xD1 0x56 0x04 0x61 0x41 0xE6 0x10 // USB READ : 0x21 0xEF 0xBE 0xAD 0xDE 0x00 5.1.2 缓冲区内容打印 // 打印 TX 缓冲区 LOG_DEBUG ( "TX Buf len=%d" , usb_tx_buf_offs) ; for ( int i= 0 ; i< usb_tx_buf_offs; i++ ) { LOG_DEBUG ( "TX[%d]=0x%02X" , i, usb_tx_buf[ i] ) ; } // 打印 RX 缓冲区 LOG_DEBUG ( "RX Buf len=%d" , usb_rx_buf_len) ; for ( int i= 0 ; i< usb_rx_buf_len; i++ ) { LOG_DEBUG ( "RX[%d]=0x%02X" , i, usb_rx_buf[ i] ) ; } 5.2 常见问题排查 问题现象 可能原因 解决方法 SCAN 命令失效 未进入 SHIFT 状态 先发送 0xB1(IRSHIFT)/0x41(DRSHIFT) 接收数据错误 未执行右移对齐 对<8位数据执行 >> (8-len) 操作 状态机卡死 TMS 位未设为1 最后块必须设置 TMS=1 数据长度不符 分块规则错误 >8位拆分为中间块+最后块 IR 指令不生效 未执行 IRUPDATE 发送 0xF1 命令使 IR 指令生效
6. 核心要点速查 6.1 SCAN 命令使用三原则 状态准备 :必须先切换到 IRSHIFT/DRSHIFT 状态才能传输数据分块传输 :>8位数据拆分为中间块(0xE6)+ 最后块(按位数编码)收尾标记 :最后块必须设置 TMS=1,确保退出 SHIFT 状态6.2 数据解析核心公式 解析后数据 = RX缓冲区字节 >> (8 - 有效位数) (仅适用于<8位的最后块)6.3 关键状态切换节点 操作类型 核心状态命令 作用 IR 生效 0xF1 (IRUPDATE) 使 IR 指令生效 DR 生效 0x81 (DRUPDATE) 完成 DR 写入/触发读取 进入传输 0xB1/0x41 进入 IR/DR 移位模式
6.4 简化记忆口诀 先复位,调速程,MSB模式要开启 IR定操作,DR传数据 SHIFT状态发SCAN,TMS=1才收尾 UPDATE指令生效,右移对齐读数据