当前位置: 代码迷 >> 综合 >> JTAG -- 调试原理与快速入门
  详细解决方案

JTAG -- 调试原理与快速入门

热度:14   发布时间:2024-03-08 09:16:50.0

目录

 

JTAG基础

什么是JTAG?

JTAG的作用是什么?

JTAG的硬件基础知识

JTAG接口

JTAG接口连接标准

JTAG调试原理

边界扫描

TAP

JTAG调试实例

连接仿真器,查看仿真器类型。

打开CCS5/CCS7软件(没有可以自行安装)

新建/选择项目目录

“view”->“Target Configurations”

“Target Configurations” -> “User Defined” -> "NewTargetConfiguration.ccxml"

“NewTargetConfiguration.ccxml” -> "Connection" -> "Board or Device" -> “Save Configration” -> "Test Configration"

“Target Configurations” -> “User Defined” -> "NewTargetConfiguration.ccxml" ->(右键) "Launch Selected Configuration"

"Launch Selected Configuration" -> "Debug" -> "xxx Debug Probe xxx" -> "Connect Target"

“Suspended” -> "view" -> "Memory..." / "Registers" / "Disassembly"...

模拟系统崩溃,使用JTAG调试找到崩溃点

内核添加sysrq模块

使用JTAG调试(获取PC、LR寄存器对应地址)

反汇编内核镜像(获取每一行地址对应运行的函数)

解析


JTAG基础

什么是JTAG?

JTAG(Joint Test Action Group ,联合测试行动小组 ) 是一种国际标准测试协议,主要用于芯片内部测试及对系统进行仿真、调试。 JTAG 技术是一种嵌入式调试技术,它在芯片内部封装了专门的测试电路 TAP ( Test Access Port ,测试访问口),通过专用的 JTAG 测试工具对内部节点进行测试。

如今大多数比较复杂的器件都支持 JTAG 协议,如 ARM 、 DSP 、 FPGA 器件等。标准的 JTAG 接口是 4 线: TMS 、 TCK 、 TDI 、 TDO ,分别为测试模式选择、测试时钟、测试数据输入和测试数据输出。

JTAG的作用是什么?

主要用途:芯片内部测试,调试。

JTAG最初是用来对芯片进行测试的,JTAG的基本原理是在器件内部定义一个TAP(Test Access Port,测试访问口)通过专用的JTAG测试工具对内部节点进行测试。JTAG测试允许多个器件通过JTAG接口串联在一起,形成一个JTAG链,能实现对各个器件分别测试。如今,JTAG接口还常用于实现ISP(In-System Programmer,在系统编程),对FLASH等器件进行编程。

TAG编程方式是在线编程,传统生产流程中先对芯片进行预编程然后再装到板上,简化的流程为先固定器件到电路板上,再用JTAG编程,从而大大加快工程进度。JTAG接口可对DSP芯片内部的所有部件进行编程。

一个含有JTAG Debug接口模块的CPU,只要时钟正常,就可以通过JTAG接口访问CPU的内部寄存器和挂在CPU总线上的设备,如FLASH,RAM,SOC(比如4510B,44Box,AT91M系列)内置模块的寄存器,像UART,Timers,GPIO等等的寄存器。

上面说的只是JTAG接口所具备的能力,要使用这些功能,还需要软件的配合,具体实现的功能则由具体的软件决定。

JTAG的硬件基础知识

JTAG是硬件实现的。

在cpu(注意:这里的cpu是指运算处理单元,只包含了内部寄存器以及运算单元等基本部件)外围,处理器(即cpu扩展芯片,不是soc)内部包含了JTAG的硬件实现,并且向外界提供接口。

JTAG接口

JTAG内部有一个状态机,称为TAP控制器。TAP控制器的状态机通过TCK和TMS进行状态的改变,实现数据和指令的输入。

标准的JTAG接口是四线(还有可选):

TMS(强制要求)——模式选择,TMS用来设置JTAG口处于某种特定的测试模式,TMS信号在TCK的上升沿有效。TMS在IEEE1149.1标准里是强制要求的。TMS信号用来控制TAP状态机的转换。通过TMS信号,可以控制TAP在不同的状态间相互转换。

TCK(强制要求)——时钟,TCK在IEEE1149.1标准里是强制要求的。TCK为TAP的操作提供了一个独立的、基本的时钟信号,TAP的所有操作都是通过这个时钟信号来驱动的。

TDI(强制要求)——数据输入,数据通过TDI输入JTAG口,TDI在IEEE1149.1标准里是强制要求的。TDI是数据输入的接口。所有要输入到特定寄存器的数据都是通过TDI接口一位一位串行输入的(由TCK驱动)。

TDO(强制要求)——数据输出,数据通过TDO从JTAG口输出,TDO在IEEE1149.1标准里是强制要求的。TDO是数据输出的接口。所有要从特定的寄存器中输出的数据都是通过TDO接口一位一位串行输出的(由TCK驱动)。

TRST(可选引脚)——测试复位,输入引脚,低电平有效。

这个信号接口在IEEE 1149.1标准里是可选的,并不是强制要求的。TRST可以用来对TAPController进行复位(初始化)。因为通过TMS也可以对TAP Controll进行复位(初始化)。所以有四线JTAG与五线JTAG之分。

除了上述的标准JTAG线之外,硬件JTAG接口上还有其他引脚,一起介绍下:

VTREF(强制要求)——接口信号电平参考电压一般直接连接Vsupply。这个可以用来确定ARM的JTAG接口使用的逻辑电平(比如3.3V还是5.0V?)

RTCK(可选引脚)——返回测试时钟(Return Test Clock,用来同步TCK信号。由目标端反馈给仿真器的时钟信号,用来同步TCK信号的产生,不使用时直接接地。

nSRST(可选引脚)——系统复位(System Reset,复位目标系统。与目标板上的系统复位信号相连,可以直接对目标系统复位。同时可以检测目标系统的复位情况,为了防止误触发应在目标端加上适当的上拉电阻。

USER IN——用户自定义输入。可以接到一个IO上,用来接受上位机的控制。

USER OUT——用户自定义输出。可以接到一个IO上,用来向上位机的反馈一个状态。

JTAG接口连接标准

如今 JTAG 接口的连接有两种标准,即 14 针接口和 20 针接口,其定义分别如下所示。

为什么会有多种接口标准?还有原本只需要标准4/5根线就能实现JTAG调试,为什么实际接口有20针/14针呢?

由于JTAG经常使用排线连接,为了增强抗干扰能力,在每条信号线间加上地线就出现了这种20针的接口。但事实上,RTCK、USER IN、USER OUT一般都不使用,于是还有一种14针的接口。对于实际开发应用来说,由于实验室电源稳定,电磁环境较好,干扰不大。

注:图为各式JTAG的接口电路。

 

对应14针的JTAG硬件引脚说明如下:

 

对应20针的JTAG硬件引脚说明如下:

 

JTAG调试原理

两个重要概念:边界扫描和TAP

边界扫描

JTAG如何用于芯片测试呢? 其中用到的最主要部件就是边界扫描链。

边界扫描(Boundary-Scan)即在芯片的每个输入输出管脚上都增加一个移位寄存器单元(Boundary-Scan Register Cell),因为这些移位寄存器单元分布在芯片的边界上,所以被称为边界扫描寄存器。

命名为边界扫描链,是由于它位置处于处理器的边界上。我们知道cpu是通过引脚与外围交流的,所有的数据都会通过引脚输入或者输出,而JTAG就是通过监控引脚的信号达到芯片测试的目的。而边界扫描链就是在引脚上的一个部件。如下图:

 

在JTAG 调试中,边界扫描是一个很重要的概念,当需要调试芯片时,这些寄存器将芯片与外围电路隔离,实现对芯片输入输出信号的观察和控制:

对于输入管脚,可以通过与之相连的边界扫描寄存器单元把数据加载到该管脚中;

对于输出管脚,可以通过与之相连的边界扫描寄存器“捕获”(CAPTURE)该管脚上的输出信号;

通过边界扫描链,当有信号输入的时候,边界扫描链就能获取信号,当cpu要输出信号的时候,边界扫描链也能获取要输出的信号。另外也可以通过边界扫描链来直接向外部输出信号。

无论是信号的抓取还是输出,都需要有接口来保存这些信号,TDI跟TDO就是做这样一些工作的。

 

本来边界扫描链保存着引脚上的信号,当通过TDI引脚输入我们自己的信号的时候,会发生沿上面红线方向的移位操作:

TDI ——〉 边界扫描链 —— TDO

就能从TDO获取边界扫描链上的信号,我们从TDI输入的信号也会到边界扫描链上去。

在cpu跟外界通信的引脚上的数据无非就是 指令 跟 数据信号(包括地址跟数据) 两种。但是这两者的结合形成了一个完整的程序,能对它们进行监控就表明我们能进行程序的调试。

正常运行状态下,这些边界扫描寄存器单元对芯片是透明的,所以正常的运行不会受到影响。另外,芯片输入输出管脚上的边界扫描(移位)寄存器单元可以相互连接起来,在芯片的周围形成一个边界扫描链(Boundary-Scan Chain),它可以串行的输入和输出,通过相应的时钟信号和控制信号,实现对处在调试状态下的芯片的输入和输出状态的观察和控制。

TAP

TAP(TestAccessPort)是一个通用的端口,通过TAP 可以访问芯片提供的所有数据寄存器(DR)和指令寄存器(IR)。对整个TAP的控制是通过TAP控制器(TAPController)来完成的。TAP的几个接口信号及其作用全部都在《硬件知识-JTAG接口》中全部表示了。

简单地说,PC机对目标板的调试就是通过TAP接口完成对相关数据寄存器(DR)和指令寄存器(IR)的访问。

JTAG调试实例

本文以DM8168芯片为例,使用XDS560仿真器,对JTAG进行调试。

连接仿真器,查看仿真器类型。

通过设备管理器可以查看当前连接的仿真器类型和型号,方面后续调试时选择正确的仿真器型号。(根据自己实际使用的仿真器型号去选择)

打开CCS5/CCS7软件(没有可以自行安装)

新建/选择项目目录

这里不是重点,随便新建一个目录都可以。

“view”->“Target Configurations”

这里就是为了选择仿真器类型+需要仿真的芯片类型。(仿真器类型看设备管理器,芯片类型看对应的芯片型号)

“Target Configurations” -> “User Defined” -> "NewTargetConfiguration.ccxml"

这里选择对应的配置文件,可以自己新建.ccxml配置文件。

“NewTargetConfiguration.ccxml” -> "Connection" -> "Board or Device" -> “Save Configration” -> "Test Configration"

双击上一步的.ccxml配置文件,进入到配置界面,然后选择对应的仿真器类型和芯片类型(仿真器类型看设备管理器,芯片类型看对应的芯片型号)

选择结束后需要点击“Save Configration”保存本次配置,然后点击“Test Configration”测试一下仿真器能否正常连接设备。

(注:在"Test Configration"这一步可能会出很多很多的问题,每个问题都需要单独分析)

有关的JTAG连接问题,参考:

https://software-dl.ti.com/ccs/esd/documents/ccsv7_debugging_jtag_connectivity_issues.html

“Test Configration”测试JTAG连接成功打印如下:

“Target Configurations” -> “User Defined” -> "NewTargetConfiguration.ccxml" ->(右键) "Launch Selected Configuration"

确认JTAG连接功能正常后,回到之前的“Target Configurations”界面(如果找不到了,重新按4、5步去做就能找到了)

在"NewTargetConfiguration.ccxml"界面通过鼠标右键,打开"Launch Selected Configuration"选项,进入到JTAG调试

"Launch Selected Configuration" -> "Debug" -> "xxx Debug Probe xxx" -> "Connect Target"

Launch之后,就可以开始Debug调试了,选择对应的芯片,点击鼠标右键,通过“Connect Target”连接,

连接成功后,芯片末尾()中会变成Suspended/Connect...之类,

后面就可以开始读取对应的芯片寄存器了。

“Suspended” -> "view" -> "Memory..." / "Registers" / "Disassembly"...

连接上芯片后,可以调试/查看的信息有下面的几个

      "Memory..." - 输入内存地址,打印对应内存地址上的值;

“Registers” - 直接查看Core寄存器中的PC、LR、R0~R15等寄存器,判断当前系统运行在哪个函数上(需要我们将程序反汇编,再根据PC/LR中的值,找到该地址对应的函数)

“Disassembly” - 对应汇编指令(其实将运行的镜像反汇编后,找到对应地址,看到的也是在执行这段命令)

模拟系统崩溃,使用JTAG调试找到崩溃点

既然都知道怎么使用JTAG了,那就实战练练手吧~

内核添加sysrq模块

编译内核(添加sysrq功能)

make menuconfig 添加“MAGIC_SYSRQ”配置项后,执行make编译内核镜像。

运行内核(使用sysrq触发模拟系统panic)

  1. 关闭看门狗(防止系统重启,模拟一个系统异常卡死的环境)

devmem 0x4804c194 32 0x40000000

注:根据自己实际板卡,设置关闭看门狗

  1. 通过sysrq触发系统异常(echo "c" > /proc/sysrq-trigger表示产生空指针panic事件,人为导致系统崩溃)

关于sysrq的详细信息,参考:文档:Linux_Sysrq魔术键使用.note

http://note.youdao.com/noteshare?id=1fdcc450a831eab5932c4247b1869fe7&sub=0D9516C818B449E091F9273CAEE1D1BF

~ # echo "c" > /proc/sysrq-trigger SysRq : Trigger a crash
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = 8fbe0000
[00000000] *pgd=8fbe8031, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1]
last sysfs file: /sys/devices/platform/omap/omap_i2c.1/i2c-1/1-0048/temp1_input
Modules linked in: pppoe pppox ppp_synctty ppp_generic slhc
CPU: 0    Not tainted  (2.6.37-g7c4b7a14-dirty #1)
PC is at sysrq_handle_crash+0x24/0x30
LR is at __handle_sysrq+0xb4/0x180
pc : [<802660d4>]    lr : [<802663d4>]    psr: 20000093
sp : 8f247f08  ip : 8f247f18  fp : 8f247f14
r10: 60000013  r9 : 00000008  r8 : 00000000
r7 : 805d35a0  r6 : 00000063  r5 : 805ead74  r4 : 805eac74
r3 : 00000000  r2 : 00000001  r1 : 60000093  r0 : 00000063
Flags: nzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 10c5387d  Table: 8fbe0019  DAC: 00000015
Process sh (pid: 125, stack limit = 0x8f2462e8)
Stack: (0x8f247f08 to 0x8f248000)
7f00:                   8f247f44 8f247f18 802663d4 802660bc 8f247f78 00000002
7f20: 802664a0 001f1e68 8f247f78 8004f808 8f246000 00000000 8f247f5c 8f247f48
7f40: 802664d8 8026632c 8fa94380 802664a0 8f247f74 8f247f60 801527f8 802664ac
7f60: 00000002 8f27b880 8f247fa4 8f247f78 80102694 801527bc 00000000 00000000
7f80: 800ffc94 00000000 00000002 001f1e68 00000001 00000004 00000000 8f247fa8
7fa0: 8004f660 8010259c 00000002 001f1e68 00000001 001f1e68 00000002 001ee328
7fc0: 00000002 001f1e68 00000001 00000004 00000000 00000002 00000001 00000000
7fe0: 001ef4c0 7efed8b0 0000c128 00008e0c 60000010 00000001 ffffffff ffffffff
Backtrace: 
[<802660b0>] (sysrq_handle_crash+0x0/0x30) from [<802663d4>] (__handle_sysrq+0xb4/0x180)
[<80266320>] (__handle_sysrq+0x0/0x180) from [<802664d8>] (write_sysrq_trigger+0x38/0x40)
[<802664a0>] (write_sysrq_trigger+0x0/0x40) from [<801527f8>] (proc_reg_write+0x48/0x90)r5:802664a0 r4:8fa94380
[<801527b0>] (proc_reg_write+0x0/0x90) from [<80102694>] (sys_write+0x104/0x140)r5:8f27b880 r4:00000002
[<80102590>] (sys_write+0x0/0x140) from [<8004f660>] (ret_fast_syscall+0x0/0x30)r7:00000004 r6:00000001 r5:001f1e68 r4:00000002
Code: e3a02001 e5832000 f57ff04f e3a03000 (e5c32000) 
---[ end trace 08fa18d2ec21349a ]---
Kernel panic - not syncing: Fatal exception
Backtrace: 
[<80053e50>] (dump_backtrace+0x0/0x130) from [<8049325c>] (dump_stack+0x18/0x1c)r7:802660d4 r6:00000000 r5:8f247cf7 r4:80600490
[<80493244>] (dump_stack+0x0/0x1c) from [<804932d4>] (panic+0x74/0x1b0)
[<80493260>] (panic+0x0/0x1b0) from [<800542f8>] (die+0x378/0x4c0)r3:806008cc r2:00000001 r1:000045b7 r0:80540dfc
[<80053f80>] (die+0x0/0x4c0) from [<8005787c>] (__do_kernel_fault+0x7c/0x90)
[<80057800>] (__do_kernel_fault+0x0/0x90) from [<80498b84>] (do_page_fault+0x94/0x260)r9:00000817 r8:8f227334 r7:00000000 r6:8f227300 r5:8f247ec0
r4:00010000
[<80498af0>] (do_page_fault+0x0/0x260) from [<8003f2ec>] (do_DataAbort+0x3c/0xa0)
[<8003f2b0>] (do_DataAbort+0x0/0xa0) from [<80496a2c>] (__dabt_svc+0x4c/0x60)
Exception stack(0x8f247ec0 to 0x8f247f08)
7ec0: 00000063 60000093 00000001 00000000 805eac74 805ead74 00000063 805d35a0
7ee0: 00000000 00000008 60000013 8f247f14 8f247f18 8f247f08 802663d4 802660d4
7f00: 20000093 ffffffff
[<802660b0>] (sysrq_handle_crash+0x0/0x30) from [<802663d4>] (__handle_sysrq+0xb4/0x180)
[<80266320>] (__handle_sysrq+0x0/0x180) from [<802664d8>] (write_sysrq_trigger+0x38/0x40)
[<802664a0>] (write_sysrq_trigger+0x0/0x40) from [<801527f8>] (proc_reg_write+0x48/0x90)r5:802664a0 r4:8fa94380
[<801527b0>] (proc_reg_write+0x0/0x90) from [<80102694>] (sys_write+0x104/0x140)r5:8f27b880 r4:00000002
[<80102590>] (sys_write+0x0/0x140) from [<8004f660>] (ret_fast_syscall+0x0/0x30)r7:00000004 r6:00000001 r5:001f1e68 r4:00000002

通过上面的异常崩溃打印,可以看到系统最终是在sysrq_handle_crash上停住了,对应的PC、LR地址都打印出来了。

那么,通过JTAG调试,最后获取到的PC、LR、R0~R15上的值,和上面的打印会一样吗?

使用JTAG调试(获取PC、LR寄存器对应地址)

通过之前描述的JTAG调试,获取到的PC、LR值分别为(0x801ff280、0x80493350)

和系统崩溃时打印出来的堆栈信息不一样,这是为什么呢?(后面会解释)

接着继续解析PC、LR上的地址分别代表什么含义。

反汇编内核镜像(获取每一行地址对应运行的函数)

在Linux下反汇编内核镜像(vmlinux),得到对应的反汇编文件。

反汇编相关的知识参考文档:objdump -- 反汇编uboot、uImage、vmli...

http://note.youdao.com/noteshare?id=e618e53e9d471fa3c5501c873c042534&sub=51163DD1A6C94511AA47C04A4402EBC5

输入如下命令得到反汇编文件vmlinux.txt:

objdump -DS vmlinux > vmlinux.txt

将JTAG调试获取的PC、LR寄存器对应地址,在vmlinux反汇编文件中找到对应函数。

解析

通过JTAG获取到PC、LR等指针;

通过反汇编获取到这些指针地址对应的函数;

最后分析代码的执行流程。

 

参考:

JTAG基本原理与调试

https://www.cnblogs.com/TaigaCon/archive/2012/12/20/2826941.html