title: OpenOCD-JTAG调试

tags: ARM

date: 2018-10-13 23:36:28

Todo

[ ] JTAG 调试linux内核

[ ] linux下使用OpenOCD调试

[x] win下使用OpenOCD调试

概述

学习文档 韦东山 Eclipse,OpenOCD,OpenJTAGv3.1嵌入式开发教程版本5.pdf

硬件连接: PC>JTAG调试器>CPU

软件控制:IDE(KEIL/ADS/)> GDB(指令)> OpenOCD(实际命令)> JTAG调试器> 单板

JTAG控制CPU功能:

当CPU的地址信号ADDR=xxx,停止CPU-硬件断点

当CPU的数据信号DATA=xxx,停止CPU--软件断点

重新运行CPU

读取R0,..寄存器

控制外设,内存

百问网的OpenJTAG.exe这个GUI实际是封装了openocd.exe命令行

设置Workdir到程序代码目录

点击telnet,或者直接cmd输入telnet 127.0.0.1 4444

使用help查看帮助或者查看`Eclipse,OpenOCD,OpenJTAGv3.1嵌入式开发教程版本5.pdf

断点

硬件断点:一个程序只能打两个断点(ARM7),可以调试ROM,NOR

设置CPU里面的JTAG比较器,使硬件断点 Addr=A,当CPU发出A地址时停止

CPU是如何运行?CPU需要取指令,也就是需要发出地址信号去取得指令,JTAG就检测到这个地址

软件断点,可以有无数个软件断点.前提是断点的地址可写,所以无法调试NOR,或者ROM上的程序,具体可以看下面的led.c的示例

设置CPU里面的JTAG比较强,使其值=一个特殊值

替换掉希望断点位置(A)的值=这个特殊值,做好备份

当CPU读取到这个特殊值,程序断点

重新运行时,恢复这个(A)位置的指令

快速使用

常用命令

halt 停止cpu

reg 查看寄存器

mdw 0 //memory display word 查看内存

mww 0 0x12345678 //memory write word

load_image leds.bin 0 //下载程序到0地址,然后可以使用mdw读取0,看看是不是程序的bin

resume 0 //指定地址运行,如果不指定地址,则恢复运行

reset 复位目标板子

reset halt

step 0 //执行第一句话,并halt

step //单步执行

bp设置断点

bp 0x6c 4 hw 在0x6c的地址位置设置断点,硬件断点

rpb 0x6c 取消断点

测试led的断点

//led.c

void wait(volatile unsigned long dly)

{

for(; dly > 0; dly--);

}

int main(void)

{

unsigned long i = 0;

GPFCON = GPF4_out|GPF5_out|GPF6_out;

while(1){

wait(30000);-------------------尝试在这里断点,就能观察灯的变化

GPFDAT = (~(i<<4));

if(++i == 8)

i = 0;

}

return 0;

}

//反汇编摘要

00000044 :

44:e52de004 strlr, [sp, #-4]!

48:e24dd004 subsp, sp, #4; 0x4

4c:e3a03000 movr3, #0; 0x0

50:e58d3000 strr3, [sp]

54:e3a03456 movr3, #1442840576; 0x56000000

58:e2833050 addr3, r3, #80; 0x50

5c:e3a02c15 movr2, #5376; 0x1500

60:e5832000 strr2, [r3]

64:e3a00c75 movr0, #29952; 0x7500

68:e2800030 addr0, r0, #48; 0x30

6c:ebffffe9 bl18 ---------------------------尝试在这里断点

70:e3a02456 movr2, #1442840576; 0x56000000

74:e2822054 addr2, r2, #84; 0x54

78:e59d3000 ldrr3, [sp]

7c:e1a03203 movr3, r3, lsl #4

80:e1e03003 mvnr3, r3

84:e5823000 strr3, [r2]

88:e59d3000 ldrr3, [sp]

8c:e2833001 addr3, r3, #1; 0x1

90:e58d3000 strr3, [sp]

94:e3530008 cmpr3, #8; 0x8

98:1afffff1 bne64

9c:e3a03000 movr3, #0; 0x0

a0:e58d3000 strr3, [sp]

a4:eaffffee b64

Disassembly of section .debug_line:

使用openocd命令来调试断点

halt

load_image leds.bin 0 //下载程序

resume 0 //指定地址运行,如果不指定地址,则恢复运行

halt

bp 0x6c 4 hw //在0x6c的地址位置设置断点,硬件断点

resume//反复这个断点,就能观察灯的变化了

测试软件断点是否改变ram值

//读取原来的值

> mdw 0x6c

0x0000006c: ebffffe9

//设置软件断点

> bp 0x6c 4

breakpoint set at 0x0000006c

//读回这个ram值,发现改变了

> mdw 0x6c

0x0000006c: deeedeee

//删除这个断点

> rbp 0x6c

//读回来

> mdw 0x6c

0x0000006c: ebffffe9

NAND调试(进阶)

程序概述:

链接地址在0x30000,0000,运行的时候应该位于0x3000,0000

直接烧写程序到内存中,也就是程序运行在0地址.并没有在加载地址

程序功能:main中点灯,但是直接烧录到内部ram,跑飞了

程序的bug在于,所有的代码都应该是位置无关的否则需要搬运代码

.text

.global _start

_start:

@函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义

ldr sp, =4096 @设置堆栈

bl disable_watch_dog @关WATCH DOG

bl memsetup @初始化SDRAM

bl nand_init @初始化NAND Flash

@将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中

@nand_read_ll函数需要3个参数:

ldr r0, =0x30000000 @1. 目标地址=0x30000000,这是SDRAM的起始地址

mov r1, #0 @2. 源地址 = 0

mov r2, #4096 @3. 复制长度= 2048(bytes),

bl nand_read @调用C函数nand_read

ldr sp, =0x34000000 @设置栈

ldr lr, =halt_loop @设置返回地址

ldr pc, =main @b指令和bl指令只能前后跳转32M的范围

@,所以这里使用向pc赋值的方法进行跳转

halt_loop:

b halt_loop

bug原因:mem_cfg_val是在栈,是位置无关的,但是他的初始值是去在链接地址取的,也就是0x3000000后面

void memsetup()

{

unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON

0x00000700, //BANKCON0

0x00000700, //BANKCON1

0x00000700, //BANKCON2

0x00000700, //BANKCON3

0x00000700, //BANKCON4

0x00000700, //BANKCON5

0x00018005, //BANKCON6

0x00018005, //BANKCON7

0x008C07A3, //REFRESH

0x000000B1, //BANKSIZE

0x00000030, //MRSRB6

0x00000030, //MRSRB7

};

}

调试开始

>reset halt

> load_image nand.bin 0

1520 bytes written at address 0x00000000

downloaded 1520 bytes in 0.063003s (23.560 KiB/s)

执行第一句话step 0也就是执行movsp, #4096; 0x1000,可以使用reg看到sp=0x1000

step执行跳转,可以发现pc=0x38,对应汇编

30000000 <_start>:

30000000:e3a0da01 movsp, #4096; 0x1000

30000004:eb00000b bl30000038

然后一步一步step,并使用poll查看当前pc值等,使用mdw查看该设置的内存

跳转到memsetup,pc=0x08,反汇编先入栈,可以发现sp=4096-5*4=4076=0xFEC

可以使用 step 0,然后设置 bp 0x50 4 hw 断点 直接跳到想到的位置

然后在汇编代码,发现问题了

// ip=300005bc

30000050:e1a0400c movr4, ip

30000054:e8b4000f ldmiar4!, {r0, r1, r2, r3}

//从r4中指向的内存给r0~r3

//也就是说从 300005bc读取一些数据,但是这个时候300005bc(sdram)并没有被初始化且进行代码搬运,所以这里的数据肯定有问题

//这里的其实就是那个局部数组的值 mem_cfg_val

300005bc <.rodata>:

300005bc:22011110 andcsr1, r1, #4; 0x4

300005c0:00000700 andeqr0, r0, r0, lsl #14

300005c4:00000700 andeqr0, r0, r0, lsl #14

300005c8:00000700 andeqr0, r0, r0, lsl #14

300005cc:00000700 andeqr0, r0, r0, lsl #14

300005d0:00000700 andeqr0, r0, r0, lsl #14

300005d4:00000700 andeqr0, r0, r0, lsl #14

300005d8:00018005 andeqr8, r1, r5

300005dc:00018005 andeqr8, r1, r5

300005e0:008c07a3 addeqr0, ip, r3, lsr #15

300005e4:000000b1 streqhr0, [r0], -r1

300005e8:00000030 andeqr0, r0, r0, lsr r0

300005ec:00000030 andeqr0, r0, r0, lsr r0

Disassembly of section .comment:

OpenOCD

全称是(Open On-Chip Debugger)

使用前都需要打开OpenOCD,连接上开发板,然后打开telent,或者使用命令telnet 127.0.0.1 4444

启动OpenOCD

专家模式:对应比较自由高级的配置,我们一般直接用普通模式即可

Interface对应 OpenOCD .4.0interface`中的选项

Target对应OpenOCD .4.0 target和OpenOCD .4.0oard

启用telnet

Win7默认没有打开这个功能,需要在程序和功能>打开或关闭 windows 功能> TelentClient打开

OpenOCD命令

这些命令都在telnet中运行,官方命令索引在这里,PDF文档OpenOCD User's Guide.pdf

记住的命令

reset halt

resume

step

load_image

目标板状态处理命令(Target state handling)

poll 查询目标板当前状态

halt 中断目标板的运行

resume [address] 恢复目标板的运行,如果指定了 address,则从 address 处开始运行

step [address] 单步执行,如果指定了 address,则从 address 处开始执行一条指令

reset 复位目标板

断点命令

bp [hw] 在地址 addr 处设置断点,指令长度为 length, hw 表示硬件断点

rbp 删除地址 addr 处的断点 内存访问指令(Memory access commands)

内存访问指令(Memory access commands)

mdw ['phys'] [count] 显示从(物理)地址 addr 开始的 count(缺省是 1)个字(4 字节)

mdh ['phys'] [count] 显示从(物理)地址 addr 开始的 count(缺省是 1)个半字(2 字节)

mdb ['phys'] [count] 显示从(物理)地址 addr 开始的 count(缺省是 1)个字节

mww ['phys'] 向(物理)地址 addr 写入一个字,值为 value

mwh ['phys'] 向(物理)地址 addr 写入一个半字,值为 value

mwb ['phys'] 向(物理)地址 addr 写入一个字节,值为 value

内存装载命令,注意:下载程序之前先使用“ halt” 命令暂停单板,才能下载代码;如果使用“ poll” 命令发现单板的 MMU 或 D-cache 已经使能,则需要使用“ arm920t cp15 2 0” 、“ step”两条命令禁止 MMU 和 D-cache。

load_image

[‘bin’|‘ihex’|‘elf’]

======将文件载入地址为 address 的内存,格式有‘bin’、 ‘ihex’、 ‘elf’

dump_image

======将内存从地址 address 开始的 size 字节数据读出,保存到文件中

verify_image

[‘bin’|‘ihex’|‘elf’]

======将文件与内存 address 开始的数据进行比较,格式有‘bin’、 ‘ihex’、 ‘elf’

CPU 架构相关命令(Architecture Specific Commands)

reg 打印寄存器的值

arm7_9 fast_memory_access ['enable'|'disable']

=======使能或禁止“快速的内存访问”

arm mcr cpnum op1 CRn op2 CRm value 修改协处理器的寄存器

=======比如: arm mcr 15 0 1 0 0 0 关闭 MMU

arm mrc cpnum op1 CRn op2 CRm 读出协处理器的寄存器

=======比如: arm mcr 15 0 1 0 0 读出 cp15 协处理器的寄存器 1

arm920t cp15 regnum [value] 修改或读取 cp15 协处理器的寄存器

=======比如 arm920t cp15 2 0 关闭 MMU

其他命令

script 执行 file 文件中的命令

OpenOCD烧录程序

load_image

[‘bin’|‘ihex’|‘elf’]

======将文件载入地址为 address 的内存,格式有‘bin’、 ‘ihex’、 ‘elf’

====load_image led.bin 0

SDRAM初始化

OpenOCD可以快速读写SDRAM,前提是需要程序先初始化好SDRAM,这样之后可以烧录u-boot到sdram,然后通过u-boot烧录其他大程序.这里需要一段初始化sdram的位置无关的代码init.bin

//从 Nand Flash 启动

load_image init/init.bin 0x0

resume 0x0

//NOR 启动

load_image init/init.bin 0x40000000

resume 0x40000000

烧录u-boot,之后打开串口,就能看到跑起来了

halt

load_image u-boot/u-boot.bin 0x33f80000

resume 0x33f80000

uboot烧录其他程序(led/uboot)

u-boot 的命令把所有的数字当作 16 进制,所以不管是否在数字前加前缀“ 0x”,这个数

字都是 16 进制的。

擦除 Flash 的长度、烧写的数据长度,这些数值都是根据要烧写的

文件的长度确定的。 u-boot.bin 的长度是 178704 字节,即 0x2BA10 字节,使用的长

度都是 0x30000,一是为了与 Flash 的可擦除长度相配(16K 的整数倍),二是方便。

GDB

linux下 arm-linux-gdb,win下arm-elf-gdb

arm-elf-gdb nand_elf

target remote 127.0.0.1:3333

load

GDB命令

启动/退出

gdb [FILE] arm-elf-gdb [FILE] arm-linux-gdb [FILE]

启动 gdb,调试 FILE(也可以先不指定文件)

quit

退出 gdb

target remote ip:port

远程连接

文件操作

file

载入文件 FILE,注意:不会下载到单板上

load [FILE]

把文件下载到单板上,如果不指定 FILE,则下载之前指定 过的(比如 file 命令指定的,或是 gdb 运行时指定的文件)

查看源程序

list

列出某个函数

list

以当前源文件的某行为中间显示一段源程序

list

接着前一次继续显示

break *

在某个地址上设置断点,比如 break *0x84

list -

显示前一次之前的源程序

info source

查看当前源程序

info stack

查看堆栈信息

info args

查看当前的参数

断点操作

break

在函数入口设置断点

break

在当前源文件的某一行上设置断点

在指定源文件的某一行上设置断点

info br

查看断点

delete

删除断点

diable

禁止断点

enable

使能断点

监视点(watch)操作

watch

当指定变量被写时,程序被停止

rwatch

当指定变量被读时,程序被停止

数据操作

print < EXPRESSION >

查看数据

set varible=value

设置变量

x /NFU ADDR

检查内存值 ① N 代表重复数 ② F 代表输出格式 x : 16 进制整数格式 d : 有符号十进制整数格式 u : 无符号十进制整数格式 f : 浮点数格式 ③ U 代表输出格式: b :字节(byte) h :双字节数值 w :四字节数值 g :八字节数值 比如“ x /4ub 0x0”将会显示 0 地址开始到 4 个字节

执行程序

step next nexti

都是单步执行: step 会跟踪进入一个函数, next 指令则不会进入函数 nexti 执行一条汇编指令

continue

继续执行程序,加载程序后也可以用来启动程序

帮助

help [command]

列出帮助信息,或是列出某个命令的帮助信

其他命令

monitor

调用 gdb 服务器软件的命令,比如:“ monitor mdw 0x0” 就是调用 openocd 本身的命令“ mdw 0x0”

使用条件

代码已经重定位,处于它的链接地址.为什么?【在源文件设置断点,其实是在链接地址设置断点,是根据链接地址去修改内存(软断点)】

链接脚本必须是按照固定格式text,data,bss分开

被调试的程序中含有debug信息,也就是编译elf时有-g选项

%.o:%.c

arm-linux-gcc -Wall -c -g -O2 -o $@ $<

%.o:%.S

arm-linux-gcc -Wall -c -g -O2 -o $@ $<

FAQ: 无法调试重定位的代码,那么代码搬运与sdram初始化怎么办? 使用opencod来执行,也就是再弄一个程序初始化sdram,使用openocd烧录

使用步骤

打开openocd,打开telent

如果是需要使用sdram的程序,则先在OpenOCD中先下载初始化sdram的程序

> load_image init/init.bin 0

> resume 0

> halt

target state: halted

target halted in ARM state due to debug-request, current mode: Supervisor

cpsr: 0x200000d3 pc: 0x000000b8

MMU: disabled, D-Cache: disabled, I-Cache: enabled

// 测试一下 sdram可用

> mdw 0x30000000

0x30000000: ea000017

> mww 0x30000000 0x12345678

> mdw 0x30000000

0x30000000: 12345678

打开cmd,输入arn-elf-gdb leds_elf启动gdb,指定程序

连接到OpenOCDtarget remote 127.0.0.1:3333

下载程序load

直接使用命令脚本 gdb.init : arm-elf-gdb -x gdb.init leds_elf

target remote localhost:3333

monitor halt

monitor arm920t cp15 2 0

monitor step

load

break main

continue

注意 gdb运行之后没有断点之后停不了了,需要在telnet使用halt,然后GDB界面才能继续输入

常用命令

si 执行一条指令

braek main.c:21 //在main.c的21行打断点

c 或者 continue 继续运行

print i //查看变量值

Eclipes

Eclipse 是 gdb(包括 arm-elf-gdb, arm-linux-gdb)的图形化前台,使用 Eclipse 进行调试实质

上是使用 gdb 进行调试。

使用 gdb 进行调试时, gdb 会用到程序的链接地址。比如在 main 函数打断点, gdb 会根

据 main 函数的链接地址找到内存上对应的指令,修改这条指令为一条特殊的指令。当程序执

行到这条特殊的指令时,就会停止下来。[也就是软件断点]

使用条件

程序应该位于它的链接地址上

如果用到SDRAM,先初始化SDRAM,然后下载程序到链接地址

简单工程

点击图标Workbench

新建一个C工程File -> New -> C Project,选择“ Makefile project->Empty Projects”、“ Other Toolchain”

导入文件在File -> Import中的General>File System

工程设置

在“ Project” 菜单里,点击去掉“ Build Automatically”前面的标

记,表示不自动编译工程

在“ Project” 菜单里,点击clean,去除Start a build immediately

编译,其实在目录下直接make也是可以的了,已经安装后工具链了

使用Project中的build all和build project都可以了

使用clean也行了

注意,make是不一样的,一个是arm-linux,另一个是arm-elf

调试配置

参考下面uboot的图配,非uboot不需要配置source选项,命令行也不需要第一个路径配置

去除debug中的stop on startup at main

project> debug config选择Zylin Native,new或者双击都可以,出现配置

Main> C/C++ Application选择调试的elfleds.elf

Debugger> Debugger选择EmbeddedGDB,下面的main选择arm-elf-gdb或者是C:Program Filesyagartoinarm-elf-gdb.exe

GDB command file 可以选择,也可以不选,其实就是提前运行的命令

target remote localhost:3333

monitor halt

monitor arm mcr 15 0 1 0 0 0

monitor step 0

load

break main

continue

但是虽然这里设置了断点,貌似有点问题,依然需要在command输入

load

break main

continue

注意

如果有时候没有看到debug窗口,右上角点一下debug视图,然后F5试试

有时候使用clean,需要看下是不是有debug存在着,需要关掉

u-boot工程

调试网上下载的 u-boot 时,需要定义 CONFIG_SKIP_LOWLEVEL_INIT,它表示

“跳过底层的初始始化”,就是不要初始化存储控制器,不要再次复制 u-boot 本身到 SDRAM

中。对于韦东山的的 u-boot,已经增加的自动识别代码,无需定义这个宏。

import相关文件

设置命令如下,这个是为了建立路径对应

set substitute-path /work/eclipse_projects/u-boot/u-boot-1.1.6_OpenJTAG E:/Eeclipse_projects/u-boot/u-boot-1.1.6_OpenJTAG

load

break start_armboot

c

然后再source中删除原来的default,添加如下(这个实际是上面的命令在生效)

workprojectsOpenPDAu-boot-1.1.6_OpenJTAG

E:Eeclipse_projectsu-bootu-boot-1.1.6_OpenJTAG

STM32烧写程序

halt

flash probe 0

flash write_image erase STM3210B.bin 0x08000000

verify_image STM3210B.bin 0x08000000

使用openocd调试Linux内核,OpenOCD-JTAG调试相关推荐

  1. 使用openocd调试Linux内核,openocd安装与调试

    环境: 硬件:PC机ARM仿真器v8.00已下载好bit流的Xinlinx SoC开发板(其上有arm cortex-a9核) 软件:Redhat Linux6(或虚拟机) + openocd 使用o ...

  2. openocd调试Linux内核,openocd安装与调试

    环境: 硬件:PC机ARM仿真器v8.00已下载好bit流的Xinlinx SoC开发板(其上有arm cortex-a9核) 软件:Redhat Linux6(或虚拟机)+ openocd 使用op ...

  3. virtualbox调试linux内核,virtualbox+kgdbt调试linux内核

    参考http://blog.csdn.net/zr_lang/article/details/8107015 环境需求: 两台linux虚拟机,我这里用的virtualbox安装的ubuntu12.0 ...

  4. eclips调试linux内核,使用Eclipse调试Qemu及Linux Kernel

    1. 下载Qemu代码 1. 下载版本 下载qemu-2.6.0版本代码,下载地址 2. 解压tar jxvg qemu-2.6.0.tar.bz2 3. 配置 配置命令如下./configure – ...

  5. 使用Android模拟器调试linux内核

    使用Android模拟器调试linux内核 为什么需要调试linux内核 如何在Android上调试内核 开发环境 创建模拟器 下载goldfish内核源码 编译goldfish内核 编译内核遇到的问 ...

  6. 使用openocd调试Linux内核,OpenOCD-JTAG调试(示例代码)

    目录 title: OpenOCD-JTAG调试 tags: ARM date: 2018-10-13 23:36:28 --- Todo [ ] JTAG 调试linux内核 [ ] linux下使 ...

  7. 使用 ftrace 调试 Linux 内核【转】

    转自:http://blog.csdn.net/adaptiver/article/details/7930646 使用 ftrace 调试 Linux 内核,第 1 部分 http://blog.c ...

  8. Bochs调试Linux内核6 - 启动过程调试 - 跳到bootsect引导程序执行

    接此,​​​​​​Bochs调试Linux内核5 - 启动过程调试 - 认识Bootsect.S_bcbobo21cn的专栏-CSDN博客 看一下,0x00007c11 这里是重复执行串传送:而后一条 ...

  9. Bochs调试Linux内核5 - 启动过程调试 - 认识Bootsect.S

    先参阅 Bochs调试Linux内核 - 定位内核中的变量或数据结构_bcbobo21cn的专栏-CSDN博客​​​​​​ ,运行到<bochs:1>,输入vbreak 0x0000:0x ...

最新文章

  1. Androidclient与服务端交互之登陆演示样例
  2. SAP UI5 初学者教程之七 - JSON 模型初探试读版
  3. 懒汉式,同步代码块线程不安全
  4. 记一次提升18倍的性能优化
  5. chap01 .net 基本框架介绍
  6. Linux下网络访问Telnet、SSH全攻略
  7. 电路——I/O口定时翻转电平驱动蜂鸣器注意事项
  8. Python-继人物词频统计三国演义之后-三国词云(Wordcloud)
  9. 空间数据平台SDP - 医疗药品门店数字化营销
  10. Centos7 64位镜像下载
  11. oracle 授权所有语句,oracle grant 授权语句
  12. 《我在大学挺好的》之——选择了计算机专业
  13. 我喜欢的学科计算机 英文作文,我喜欢的学科写英语作文40字
  14. gamemaker学习笔记:拖拽
  15. 校园安全教育APP的设计与实现
  16. 译文 :图文教程,8步教你变身数据科学家
  17. 归并排序(Python)
  18. 钉钉这两年:陈航自我迭代 阿里社交进击
  19. VBA 设置图表数据源
  20. 我的物联网项目(三十) 线上账户互通和积分体系

热门文章

  1. 分治——Secret Cow Code S(洛谷 P3612)
  2. thinkphp3.2.3 调用自定义模型提示找不到类_面试BAT必问的JVM,今天我们来说一说它类加载器的底层原理...
  3. c语言声明第一个学生的成绩,C语言课程设计报告--学生成绩管理系统
  4. 浅析MySQL存储引擎序列属性
  5. 经典分享:一份高达555页的技术PPT会是什么样子?
  6. 双11享Go了吗?2017阿里双11在线峰会续写科技盛宴!
  7. 低代码实现传统装饰企业的管理跃迁
  8. DevOps敏捷60问,一定有你想了解的问题
  9. 遥感影像处理有高招,“专治”各类花式并发的述求!
  10. 技术实践丨手把手教你使用MQTT方式对接华为IoT平台