轉(zhuǎn)載:用openocd+jtag并口小板調(diào)試qq2440
openocd是一個開源的調(diào)試工具,支持一些主流的CPU,不過目前來看,支持ARM是比較多的。其主頁為http://openocd.berlios.de/ . 目前openocd開發(fā)得還比較頻繁,我現(xiàn)在用的svnrevision是1833。當(dāng)然,如果有新的話,最好嘗試最新的revision。如果遇到了問題,比如編譯通不過之類的,可以再試試稍微舊一點的版本。
編譯和安裝openocd的方法:
首先從svn 上將最新的代碼checkout下來:
svn co svn://svn.berlios.de/openocd/trunk openocd
這會將代碼checkout到當(dāng)前目錄的openocd目錄中,然后,可以進入到openocd目錄中開始編譯和安裝:
cd openocd
./bootstrap
./configure --enable-parport
make
sudo make install
上面的./configure--enable-parport是指編譯對并口jtag轉(zhuǎn)換小板的支持,如wiggler。openocd也支持其他一些jtag轉(zhuǎn)換板,可以使用./configure --help查看。因為我只有qq2440自帶的那個并口小板,所以啟用了并口支持。
*關(guān)于gdb
用gdb調(diào)試arm程序,需要一個專門針對arm的gdb,而一般x86系統(tǒng)上安裝的是針對x86的gdb,是不能用來調(diào)試arm代碼的。這同交叉編譯的概念是一致的,我們需要這樣一個gdb,他運行在我們的開發(fā)平臺上,一般是X86 PC機,但他針對的調(diào)試對象是ARM的。
arm-linux-gdb的編譯:
首先從ftp://ftp.gnu.org上去下載最新的gdb源碼,我下載的是gdb-6.8.tar.bz2。編譯安裝的過程是:
tar -jxf gdb-6.8.tar.bz2
cd gdb-6.8
./configure --target=arm-linux --program-prefix=arm-linux-
make
sudo make install
這樣就得到了arm-linux-gdb, 不過我一般不使用makeinstall將這個arm-linux-gdb裝到系統(tǒng)里面去,而是直接運行源碼里面編譯出來的gdb可執(zhí)行文件。怎么樣方便就看你的習(xí)慣了。
*關(guān)于jtag并口小板
其實目前的一些并口小板,絕大多數(shù)用的都是一個原理,就是直接利用PC機的并口來模擬JTAG時序。之所以會有這么多的變體,主要的差別是接法不一樣,也就是說用的并口上數(shù)據(jù)端口的不同的bit,但是本質(zhì)上是沒有區(qū)別的。
openocd的parport驅(qū)動支持一些常見的并口小板,在openocd的src/jtag/parport.c里面,有一個這樣的數(shù)組:
static cable_t cables[] =
{
/* name tdo trst tms tck tdi srst o_inv i_inv init exit led */
{ "wiggler", 0x80, 0x10, 0x02, 0x04, 0x08,0x01, 0x01, 0x80, 0x80, 0x80, 0x00 },
{ "wiggler2", 0x80, 0x10, 0x02, 0x04, 0x08,0x01, 0x01, 0x80, 0x80, 0x00, 0x20 },
{ "wiggler_ntrst_inverted",
0x80, 0x10,0x02, 0x04, 0x08, 0x01, 0x11, 0x80, 0x80, 0x80, 0x00 },
{ "old_amt_wiggler", 0x80, 0x01, 0x02, 0x04, 0x08, 0x10,0x11, 0x80, 0x80, 0x80, 0x00 },
{ "arm-jtag", 0x80, 0x01, 0x02, 0x04, 0x08,0x10, 0x01, 0x80, 0x80, 0x80, 0x00 },
{ "chameleon", 0x80, 0x08, 0x04, 0x01, 0x02,0x10, 0x00, 0x80, 0x00, 0x00, 0x00 },
{ "dlc5", 0x10, 0x00, 0x04, 0x02,0x01, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00 },
{ "triton", 0x80, 0x08, 0x04, 0x01,0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 },
{ "lattice", 0x40, 0x10, 0x04, 0x02, 0x01,0x08, 0x00, 0x00, 0x18, 0x18, 0x00 },
{ "flashlink", 0x20, 0x10, 0x02, 0x01, 0x04,0x20, 0x30, 0x20, 0x00, 0x00, 0x00 },
/* Altium Universal JTAG cable. Set the cable to Xilinx Mode and wire totarget as follows:
HARD TCK - Target TCK
HARD TMS - Target TMS
HARD TDI - Target TDI
HARD TDO - Target TDO
SOFT TCK - Target TRST
SOFT TDI - Target SRST
*/
{ "altium", 0x10, 0x20, 0x04, 0x02, 0x01,0x80, 0x00, 0x00, 0x10, 0x00, 0x08 },
{ NULL, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
};
這個接口提定義了一些常見的并口小板,可以看到,無非就是將jtag接口的信號接到了不同的數(shù)據(jù)位上。我看了下,qq2440所帶的并口小板接法對應(yīng)這個chameleon。不過我對這個小板又做了點改造,具體的看下面。
*關(guān)于jtag接口
jtag接口中,必要的信號是TMS, TCK, TDI, TDO,TRST和SRST是可選的信號,其中TRST用來復(fù)位TAP控制器,SRST用來復(fù)位目標(biāo)CPU。正是因為這兩個信號是可選的,所以造成了一些混亂。對openocd來說,最理想的情況是,它能夠分別控制TRST和SRST兩個信號。這樣的話,他就可以先設(shè)置SRST并保持住,讓目標(biāo)CPU保持在復(fù)位的狀態(tài),然后用TRST信號復(fù)位TAP控制器,最后再取消SRST信號,這樣就可以讓目標(biāo)CPU復(fù)位后就處于debug狀態(tài)。這樣的前提是,目標(biāo)CPU和jtag小板都要支持這兩個信號,S3C2440以及qq2440的設(shè)計是支持這兩個信號的,但是,附帶的那個并口小板就有些問題了。并口小板上只連接了四個必要的信號,并沒有接TRST和SRST。我對這個小板的改造就是手工補焊上這兩根線。
并口小板上的HC541是個8通道的驅(qū)動器,起電平轉(zhuǎn)換用,TMS,TCK,TDI,TDO,四個信號占用了4個通道,另四個通道空著,我所補焊的幾根線,就是利用其中的兩個通道補上TRST和SRST??梢钥纯瓷厦娴腸ables數(shù)組的定義,其中的chameleon定義和svn上的源碼是有點區(qū)別的。trst的對應(yīng)值是0x08,srst對應(yīng)的值是0x10,這可以看出補焊線的方法:我用并口8位數(shù)據(jù)口的第4位(0x08)作trst,第5位(0x10)作srst。有機會我在把改后的照片發(fā)上來,其實很簡單的,有點電路知識的人應(yīng)該可以自己搞定。
*openocd的使用
關(guān)于openocd的說明,最好的辦法當(dāng)然是看openocd的文檔。我這里所列的,是目前我所能實現(xiàn)的一些東西,當(dāng)然還有很多我也在研究中,比如如何用他來燒寫flash。
首先openocd需要一個配置文件,配置文件的寫法很靈活,可以使用一個文件,也可以使用-f參數(shù)指定的多個文件,還可以在一個配置文件中包含另一個文件。我這里用的是最直接的辦法,在當(dāng)前目錄下放一個openocd.cfg這樣,當(dāng)運行openocd的時候,默認就會找這個配置文件,我的配置文件的內(nèi)容為:
telnet_port 4000
gdb_port 3000
interface parport
parport_port 0x378
parport_cable chameleon
jtag_speed 0
source [find target/samsung_s3c2440.cfg]
$_TARGETNAME configure -event reset-init {
mww 0x48000000 0x22111112
mww 0x48000004 0x00000700
mww 0x48000008 0x00000700
mww 0x4800000c 0x00000700
mww 0x48000010 0x00000700
mww 0x48000014 0x00000700
mww 0x48000018 0x00000700
mww 0x4800001c 0x00018009
mww 0x48000020 0x00018009
mww 0x48000024 0x008e04eb
mww 0x48000028 0x000000b2
mww 0x4800002c 0x00000030
mww 0x48000030 0x00000030
}
前兩行指定了telnet 和gdb連接時所使用的端口,然后就指定了并口的地址和并口小板的類型。source用來包含另一個文件,find的默認查找路徑是/usr/local/lib/openocd。最后的reset-init定義了一個event,當(dāng)系統(tǒng)復(fù)位的時候,會執(zhí)行這些命令,以上這些命令用來初始化SDRAM控制器。
準(zhǔn)備好配置文件后,可以在當(dāng)前目錄下運行openocd,注意要使用root權(quán)限,并確保jtag已經(jīng)連接好,開發(fā)板電源已經(jīng)打開。
fox@NA ~ $ sudo openocd
Open On-Chip Debugger 0.2.0-in-development (2009-05-19-10:39) svn:1833M
BUGS? Read http://svn.berlios.de/svnroot/repos/openocd/trunk/BUGS
$URL: svn://svn.berlios.de/openocd/trunk/src/openocd.c $
jtag_speed: 0
Info : JTAG tap: s3c2440.cpu tap/device found: 0x0032409d (Manufacturer:0x04e, Part: 0x0324, Version: 0x0)
Info : JTAG Tap/device matched
Warn : no tcl port specified, using default port 6666
如果提示 “Info : JTAG tap: s3c2440.cpu tap/device found: 0x0032409d(Manufacturer: 0x04e, Part: 0x0324, Version: 0x0)”,表示openocd已經(jīng)通過jtag連接上了目標(biāo)CPU,我們的任務(wù)也完成了一半,注意openocd會停在這個地方,是正常的,所以下面的操作要在另一個終端窗口里面進行。
然后就可以用telnet連上openocd了:
fox@NA ~ $ telnet 127.0.0.1 4000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Open On-Chip Debugger
>
在">"提示符后就可以輸入openocd的命令了,還是那句話,看openocd的文檔。輸入help也會出一個命令的幫助列表出來。常用的命令:
reset [halt|init|run] 復(fù)位目標(biāo)CPU,復(fù)位后可以使halt,init或run。halt標(biāo)示立即掛起,init表示要運行init腳本,run標(biāo)示復(fù)位后不掛起CPU,讓CPU繼續(xù)運行。
halt 掛起目標(biāo)板的CPU。
resume 恢復(fù)CPU的運行
step 單步
mdb/mdh/mdw 顯示內(nèi)存(字節(jié),半字,字)
mwb/mwh/mww 寫內(nèi)存(字節(jié),半字,字)
reg 顯示寄存器
load_image 載入數(shù)據(jù)到內(nèi)存中指定的地方
上面的mdb/mdh/mdw/mwb/mwh/mww這些指令如果在CPU開啟了MMU的狀態(tài)下,后面的地址會被解釋為虛擬地址。如果想訪問物理地址,可以使用對應(yīng)的 arm920t mdb_phys, arm920t_mdh_phys等等,詳見help。
*用arm-linux-gdb配合openocd來調(diào)試程序
目前,我對gdb的使用還不是很熟悉,基本上是摸著石頭過河的狀況。因此下面肯定有很多地方顯得比較笨拙,哎,這個只能在學(xué)習(xí)中慢慢改進了。
首先啟動arm-linux-gdb,直接運行就可以,出現(xiàn)提示符后,可以在gdb里面連接上openocd:
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "showcopying"
and "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu--target=arm-linux".
(gdb) target remote 127.0.0.1:3000
Remote debugging using 127.0.0.1:3000
0x00000038 in ?? ()
還是按照我的習(xí)慣,先從最簡單的開始。這里就不調(diào)試什么kernel啊,從最簡單的裸奔程序開始,首先,來個只有匯編的。作為例子,下面這個簡單得不能再簡單了:
.text
.global _start
_start:
mov r0, #0
mov r1, #0
mov r2, #0
loop:
b loop
文件保存為test1.S,注意是大寫的“S”, 然后用arm-linux-gcc進行編譯和連接:
fox@NA ~ $ arm-linux-gcc -g -c test1.S -o test1.o
fox@NA ~ $ arm-linux-ld -g -Ttext 0x30000000 test1.o -o test1.elf
上面用 -Ttext指定了代碼的加載地址,0x30000000就是qq2440上SDRAM映射的地址。
把編譯出來的目標(biāo)文件加載到SDRAM中有兩個方法,一個是用openocd的load_image命令,不過load_image命令不認識elf格式,所以要用objcopy將.text節(jié)從elf文件里面取出來,然后在用openocd的load_image將其加載到指定的地方,也就是0x30000000。
還有一個方法,我一般在gdb里面直接載入elf文件,elf里面已經(jīng)包含了載入地址,入口點等信息,直接用gdb的load命令就可以了,如下:
(gdb) load test1.elf
Loading section .text, size 0x10 lma 0x30000000
Start address 0x30000000, load size 16
Transfer rate: 1 KB/sec, 16 bytes/write.
(gdb) file test1.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "/home/fox/test1.elf"? (y or n) y
Reading symbols from /home/fox/test1.elf...done.
(gdb) i r
r0 0x0 0
r1 0x53000000 1392508928
r2 0x6 6
r3 0x50000010 1342177296
r4 0x0 0
r5 0x0 0
r6 0x0 0
r7 0x0 0
r8 0x594 1428
r9 0xf673eefd 4134792957
r10 0x33f12fb4 871444404
r11 0x0 0
r12 0x2 2
sp 0x33defe90 0x33defe90
lr 0x594 1428
pc 0x30000000 0x30000000 <_start>
fps 0x0 0
cpsr 0x6000005f 1610612831
file指令是用載入符號和調(diào)試信息,后面用 ir來顯示寄存器信息,可以看到pc都已經(jīng)設(shè)置好了。用n命令就可以單步調(diào)試了,不過gdb我用的不太熟,還要摸索。
*用arm-linux-gdb調(diào)試c代碼
如果要編寫調(diào)試一段裸奔的c代碼,其實很簡單,對于c代碼的運行,只需要一個條件,就是要準(zhǔn)備好棧。一般做法是寫一小段匯編引導(dǎo)程序,設(shè)置好棧之后,再跳轉(zhuǎn)到c代碼里面。匯編代碼test2.S如下:
.text
.global _start
_start:
ldr sp, =0x34000000
bl mymain
loop:
b loop
上面的 ldr sp, =0x34000000 就是將棧指針設(shè)置在64MSDRAM空間的最后,因為棧是向內(nèi)存地址小的方向增長的。bl用來跳轉(zhuǎn)到c代碼中, test2.c如下:
#define GPBCON (*(unsigned long*)0x56000010)
#define GPBDAT (*(unsigned long*)0x56000014)
#define GPBUP (*(unsigned long*)0x56000018)
int mymain()
{
volatile unsigned long v = GPBCON;
v &= 0xFFFc03FF;
v |= 0x00015400;
GPBCON = v;
v = GPBDAT;
v |= 0x000001e0;
GPBDAT = v; /* turn off all LEDs */
v &= ~0x000001e0;
GPBDAT = v; /* turn on all LEDs */
return 0;
}
編譯過程
arm-linux-gcc -g -c test2.S -o test2.o
arm-linux-gcc -g -c test2.c -o test2_main.o
arm-linux-ld -g -Ttext 0x30000000 test2.o test2_main.o -o test2.elf
在gdb環(huán)境中載入并調(diào)試的過程為:
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "showcopying"
and "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu--target=arm-linux".
(gdb) target remote 127.0.0.1:3000
Remote debugging using 127.0.0.1:3000
0x3000009c in ?? ()
(gdb) load test2.elf
Loading section .text, size 0xa8 lma 0x30000000
Start address 0x30000000, load size 168
Transfer rate: 4 KB/sec, 168 bytes/write.
(gdb) file test2.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from /home/fox/test2.elf...done.
(gdb) i r
r0 0x0 0
r1 0x104 260
r2 0x61e 1566
r3 0x56000014 1442840596
r4 0x20000 131072
r5 0x33f00000 871366656
r6 0x0 0
r7 0x0 0
r8 0x594 1428
r9 0xf673eefd 4134792957
r10 0xc8 200
r11 0x33fffffc 872415228
r12 0x34000000 872415232
sp 0x33ffffec 0x33ffffec
lr 0x30000008 805306376
pc 0x30000000 0x30000000 <_start>
fps 0x0 0
cpsr 0x800000d3 2147483859
Current language: auto; currently asm
(gdb) b mymain
Breakpoint 1 at 0x3000001c: file test2.c, line 7.
(gdb) c
Continuing.
Breakpoint 1, mymain () at test2.c:7
7 volatile unsigned long v = GPBCON;
Current language: auto; currently c
(gdb) n
8 v &= 0xFFFc03FF;
(gdb) n
9 v |= 0x00015400;
(gdb) n
10 GPBCON = v;
(gdb) n
13 v = GPBDAT;
(gdb) n
14 v |= 0x000001e0;
(gdb) n
15 GPBDAT = v; /* turn off all LEDs */
(gdb) n
17 v &= ~0x000001e0;
(gdb) n
18 GPBDAT = v; /* turn on all LEDs */
(gdb) n
20 return 0;
(gdb)
可以看到,可以用b設(shè)置斷點,用n進行源碼級的調(diào)試,單步跟蹤,也能觀察到開發(fā)板上的所有LED先熄滅,再點亮。關(guān)于gdb的更多操作,也正在學(xué)習(xí)中。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點擊舉報。