1. 程式人生 > >OpenOCD安裝與使用(JTAG除錯)

OpenOCD安裝與使用(JTAG除錯)

****************** `本文介紹openocd開源軟體的安裝以及搭配JTAG對Xilinx u500VC707devkit的除錯` `PC OS: Ubuntu20.04 LTS` `Target ARCH: riscv64` `JTAG: Olimex-ARM-USB-TINY-H` `OpenOCD version: OpenOCD v0.10.0` ****************** ## 1、OpenOCD簡介   `OpenOCD(Open On-Chip Debugger)`是一款開源的開放式片上除錯軟體,需要在除錯介面卡(如: JTAG、SWD等)的配合下可以對片上系統進行相應除錯,以及在嵌入式裝置上測試系統內程式或邊界接掃描測試。 ## 2、下載OpenOCD   可以通過[OpenOCD官網](www.openocd.org)下載原始碼,已經有很多平臺提供配套目標平臺的`OpenOCD`原始碼,也可以通過這些來下載帶有相應目標平臺配置檔案的`OpenOCD`,本文主要介紹支援`riscv64`的`OpenOCD`下載和使用。 `riscv-openocd`下載連結: [https://github.com/sifive/riscv-openocd](https://github.com/sifive/riscv-openocd) 通過`git`下載: ```shell git clone https://github.com/sifive/riscv-openocd.git ``` ## 3、編譯與安裝 **<1>** 進入`riscv-openocd`原始碼目錄 ```shell imaginemiracle@:openocd$ cd riscv-openocd imaginemiracle@:riscv-openocd$ ls AUTHORS ChangeLog COPYING HACKING NEWS-0.10.0 NEWS-0.5.0 NEWS-0.9.0 README.Windows TODO AUTHORS.ChangeLog config_subdir.m4 doc jimtcl NEWS-0.2.0 NEWS-0.6.0 NEWTAPS src tools bootstrap configure.ac Doxyfile.in Makefile.am NEWS-0.3.0 NEWS-0.7.0 README tcl uncrustify.cfg BUGS contrib guess-rev.sh NEWS NEWS-0.4.0 NEWS-0.8.0 README.OSX testing ``` **<2>** 執行`bootstrap`生成`configure`檔案,通過`configure`配置`OpenOCD`,主要需要配置`OpenOCD`支援的偵錯程式的型別,筆者使用的`JTAG`為`FTDI`型別,因此需要`OpenOCD`支援`FTDI`。 `[注] 通過檢視JTAG手冊瞭解所使用的JTAG是什麼型別,通過./configure --help 命令檢視所需要開啟的型別以及其它配置引數` ```shell #建立安裝目錄 imaginemiracle@:riscv-openocd$ mkdir install_IM #執行bootstrap imaginemiracle@:riscv-openocd$ ./bootstrap ``` `bootstrap`執行的正常輸出: ```shell + aclocal + libtoolize --automake --copy + autoconf + autoheader + automake --gnu --add-missing --copy configure.ac:26: installing './compile' configure.ac:37: installing './config.guess' configure.ac:37: installing './config.sub' configure.ac:16: installing './install-sh' configure.ac:16: installing './missing' Makefile.am:46: warning: wildcard $(srcdir: non-POSIX variable name Makefile.am:46: (probably a GNU make extension) Makefile.am: installing './INSTALL' Makefile.am: installing './depcomp' Makefile.am:23: installing './mdate-sh' Makefile.am:23: installing './texinfo.tex' Setting up submodules Submodule 'jimtcl' (https://github.com/msteveb/jimtcl) registered for path 'jimtcl' Cloning into '/media/imaginemiracle/Disk_D/Linux_Workspace/riscv-project/File_System_test/openocd/riscv-openocd/jimtcl'... Submodule path 'jimtcl': checked out '51f65c6d38fbf86e1f0b036ad336761fd2ab7fa0' Submodule path 'jimtcl': checked out '51f65c6d38fbf86e1f0b036ad336761fd2ab7fa0' Generating build system... libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, 'build-aux'. libtoolize: copying file 'build-aux/config.guess' libtoolize: copying file 'build-aux/config.sub' libtoolize: copying file 'build-aux/install-sh' libtoolize: copying file 'build-aux/ltmain.sh' libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'. libtoolize: copying file 'm4/libtool.m4' libtoolize: copying file 'm4/ltoptions.m4' libtoolize: copying file 'm4/ltsugar.m4' libtoolize: copying file 'm4/ltversion.m4' libtoolize: copying file 'm4/lt~obsolete.m4' configure.ac:42: installing 'build-aux/ar-lib' configure.ac:37: installing 'build-aux/compile' configure.ac:30: installing 'build-aux/missing' Makefile.am: installing './INSTALL' libjaylink/Makefile.am: installing 'build-aux/depcomp' Bootstrap complete. Quick build instructions: ./configure .... ``` 成功生成`configure`檔案 ```shell #配置安裝目錄路徑必須為絕對路徑 imaginemiracle@:riscv-openocd$ ./configure --prefix=/home/imaginemiracle/Disk_D/Linux_Workspace/riscv-project/File_System_test/openocd/openocd-0.10.0/install_IM/ --enable-ftdi ###################只顯示關鍵輸出#################### checking that generated files are newer than configure... done configure: creating ./config.status config.status: creating Makefile config.status: creating libjaylink/Makefile config.status: creating libjaylink/version.h config.status: creating libjaylink.pc config.status: creating Doxyfile config.status: creating config.h config.status: executing depfiles commands config.status: executing libtool commands ``` 看到成功成生`Makefile`檔案且沒有報錯,則說明配置成功。 **<3>** 配置成功後直接編譯並安裝即可 直接使用`make; make install`編譯並安裝 ```shell #編譯後並且安裝 imaginemiracle@:riscv-openocd$ make; make install ``` 若`make; make install`直接通過,則跳過`<4>`這一節直接看下一節的`openocd`使用介紹。 **<4>** 編譯`openocd`可能會遇到如下幾種錯誤 **<4.1>** **報錯 1:** `src/svf/svf.c:663:7: error: this statement may fall through [-Werror=implicit-fallthrough=]` ```shell #錯誤1 src/svf/svf.c: In function ‘svf_read_command_from_file’: src/svf/svf.c:663:7: error: this statement may fall through [-Werror=implicit-fallthrough=] 663 | i = -1; | ~~^~~~ src/svf/svf.c:664:4: note: here 664 | case '\r': | ^~~~ src/svf/svf.c:667:8: error: this statement may fall through [-Werror=implicit-fallthrough=] 667 | if (!cmd_pos) | ^ src/svf/svf.c:669:4: note: here 669 | default: | ^~~~~~~ cc1: all warnings being treated as errors ``` **解決方案 1:** 通過原始碼分析可以看出,這塊的報錯只是因為在`switch case`語句中一個`case`沒有寫`break`,編譯器識別到可能會跳到下一個`case`語句中,因此在這裡直接忽略該錯誤繼續編譯即可。 ```shell imaginemiracle@:openocd$ make -j8 CFLAGS='-Wno-implicit-fallthrough' ``` **<4.2>** **報錯 2:** `src/target/arm_disassembler.c:1499:30: error: bitwise comparison always evaluates to false [-Werror=tautological-compare]` ```shell #錯誤2 src/target/arm_disassembler.c: In function ‘evaluate_misc_instr’: src/target/arm_disassembler.c:1499:30: error: bitwise comparison always evaluates to false [-Werror=tautological-compare] 1499 | if (((opcode & 0x00600000) == 0x00100000) && (x == 0)) { | ^~ src/target/arm_disassembler.c:1521:29: error: bitwise comparison always evaluates to false [-Werror=tautological-compare] 1521 | if ((opcode & 0x00600000) == 0x00300000) { | ^~ src/target/arm_disassembler.c:1542:30: error: bitwise comparison always evaluates to false [-Werror=tautological-compare] 1542 | if (((opcode & 0x00600000) == 0x00100000) && (x == 1)) { | ^~ ``` **解決方案 2:** 按照道理說這種寫法是沒什麼問題的,但還是會報錯,因此將報錯的三個位置進行修改,修改後的程式碼如下。 ```shell imaginemiracle@:openocd$ vim src/target/arm_disassembler.c +1499 ``` ```c //File src/target/arm_disassembler.c +1499:In function 'evaluate_misc_instr' /* SMLAW < y> */ //============================Alter by me============================== if (!(((opcode & 0x00600000) - 0x00100000)) && (x == 0)) { //============================ End Alter ============================== uint8_t Rd, Rm, Rs, Rn; instruction->type = ARM_SMLAWy; Rd = (opcode & 0xf0000) >> 16; Rm = (opcode & 0xf); Rs = (opcode & 0xf00) >> 8; Rn = (opcode & 0xf000) >> 12; snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMLAW%s%s r%i, r%i, r%i, r%i", address, opcode, (y) ? "T" : "B", COND(opcode), Rd, Rm, Rs, Rn); } /* SMUL < x> */ //============================Alter by me============================== if (!((opcode & 0x00600000) - 0x00300000)) { //============================ End Alter ============================== uint8_t Rd, Rm, Rs; instruction->type = ARM_SMULxy; Rd = (opcode & 0xf0000) >> 16; Rm = (opcode & 0xf); Rs = (opcode & 0xf00) >> 8; snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMULW%s%s%s r%i, r%i, r%i", address, opcode, (x) ? "T" : "B", (y) ? "T" : "B", COND(opcode), Rd, Rm, Rs); } /* SMULW < y> */ //============================Alter by me============================== if (!(((opcode & 0x00600000) - 0x00100000)) && (x == 1)) { //============================ End Alter ============================== uint8_t Rd, Rm, Rs; instruction->type = ARM_SMULWy; Rd = (opcode & 0xf0000) >> 16; Rm = (opcode & 0xf); Rs = (opcode & 0xf00) >> 8; snprintf(instruction->text, 128, "0x%8.8" PRIx32 "\t0x%8.8" PRIx32 "\tSMULW%s%s r%i, r%i, r%i", address, opcode, (y) ? "T" : "B", COND(opcode), Rd, Rm, Rs); } ``` **<4.3>** **報錯 3:** `src/target/nds32_cmd.c:824:21: error: ‘sprintf’ writing a terminating nul past the end of the destination [-Werror=format-overflow=]` ```shell #錯誤3 src/target/nds32_cmd.c: In function ‘jim_nds32_bulk_read’: src/target/nds32_cmd.c:824:21: error: ‘sprintf’ writing a terminating nul past the end of the destination [-Werror=format-overflow=] 824 | sprintf(data_str, "0x%08" PRIx32 " ", data[i]); | ^~~~~~~ src/target/nds32_cmd.c:824:38: note: format string is defined here 824 | sprintf(data_str, "0x%08" PRIx32 " ", data[i]); | ^ src/target/nds32_cmd.c:824:3: note: ‘sprintf’ output 12 bytes into a destination of size 11 824 | sprintf(data_str, "0x%08" PRIx32 " ", data[i]); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` **解決方案 3:** 從錯誤型別`format-overflow`,格式溢位,一看就是不重要的錯誤,直接忽略就好繼續編譯。 ```shell imaginemiracle@:openocd$ make -j8 CFLAGS='-Wno-implicit-fallthrough -Wno-format-overflow' ``` **<4.4>** **報錯 4:** `/usr/include/x86_64-linux-gnu/sys/sysctl.h:21:2: error: #warning "The
header is deprecated and will be removed." [-Werror=cpp]` ```shell #錯誤4 In file included from src/helper/options.c:38: /usr/include/x86_64-linux-gnu/sys/sysctl.h:21:2: error: #warning "The header is deprecated and will be removed." [-Werror=cpp] 21 | #warning "The header is deprecated and will be removed." | ^~~~~~~ cc1: all warnings being treated as errors ``` **解決方案 4:** 可以看出這裡是使用了`Linux`已經移除的標頭檔案`
`報錯的,因此進入報錯檔案,做以下修改。 ```shell imaginemiracle@:openocd$ vim src/helper/options.c +38 ``` ```c //File src/helper/options.c +38 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "configuration.h" #include "log.h" #include "command.h" #include #include #include #if IS_DARWIN #include #endif //===================Alter by me=================== #ifdef HAVE_SYS_SYSCTL_H //#include
#endif #if IS_WIN32 && !IS_CYGWIN #include #endif //=================== End Alter =================== static int help_flag, version_flag; ``` ## 4、OpenOCD的使用 ### 4.1、OpenOCD的配置 安裝成功後則會在配置的安裝目錄裡生成如下檔案,這裡的`openocd`就是需要用到的可執行程式將它拷貝到需要執行的目錄,或者直接在當前目錄使用也可以。(`[注]: 若未配置安裝路徑,預設安裝到“./src目錄”中`) ```shell imaginemiracle@:openocd$ cd install_IM/ imaginemiracle@:install_IM$ ls bin share imaginemiracle@:install_IM$ ls bin/ openocd imaginemiracle@:openocd$ cp install_IM/bin/openocd ./ ``` 使用`openocd+JTAG`需要用到兩個配置檔案,分別是`JTAG`的配置檔案和目標平臺的配置檔案。一般`JTAG`廠商都會提供購買到的`JTAG`的`openocd`配置檔案,這裡就可以直接使用。 #### <1> JTAG裝置配置檔案 前文提到筆者所使用的`JTAG`型號為`Olimex-ARM-USB-TINY-H`,其配置檔案如下: ```shell # File olimex-arm-usb-tiny-h.cfg # # Olimex ARM-USB-TINY-H # # http://www.olimex.com/dev/arm-usb-tiny-h.html # interface ftdi #interface jlink ftdi_device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H" ftdi_vid_pid 0x15ba 0x002a ftdi_layout_init 0x0808 0x0a1b ftdi_layout_signal nSRST -oe 0x0200 ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100 ftdi_layout_signal LED -data 0x0800 ``` 當電腦連結上`JTAG`後,還需要安裝`JTAG`的驅動,若“`lsusb`”可以檢視到`JTAG`裝置,則說明驅動安裝成功。 ```shell imaginemiracle@:openocd$ lsusb Bus 002 Device 002: ID 0424:5744 Microchip Technology, Inc. (formerly SMSC) Hub Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 004: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode) Bus 001 Device 003: ID 0424:2744 Microchip Technology, Inc. (formerly SMSC) Hub Bus 001 Device 007: ID 413c:2113 Dell Computer Corp. Dell KB216 Wired Keyboard Bus 001 Device 006: ID 413c:301a Dell Computer Corp. Dell MS116 USB Optical Mouse Bus 001 Device 008: ID 15ba:002a Olimex Ltd. ARM-USB-TINY-H JTAG interface Bus 001 Device 002: ID 10c4:ea60 Silicon Labs CP210x UART Bridge Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub ``` 這裡的`ID`後面的值分別為`USB`裝置的`VID`和`PID`,這裡的值應該與`JTAG`配置檔案中的`vid_pid`相同才可。 #### <2> 目標平臺配置檔案 `OpenOCD`的配置檔案使用的是`tcl`語言,這裡的目標平臺配置檔案是筆者仿照其它配置檔案修改的。 ```tcl # File riscv64_IM.cfg proc init_targets {} { adapter_khz 1000 reset_config trst_and_srst set _CHIPNAME riscv jtag newtap $_CHIPNAME cpu -irlen 5 set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME riscv -endian little -chain-position $_TARGETNAME -coreid 0 # $_TARGETNAME configure -rtos riscv # $_TARGETNAME configure -work-area-phys 0x3ff0000 -work-area-size 0x10000 -work-area-backup 1 # $_TARGETNAME riscv expose_csrs 3008-3015,4033-4034 } proc sw_reset_halt {} { reset halt } ``` ### 4.2、OpenOCD連結JTAG 有了`JTAG`和目標平臺的兩個配置檔案後,就可以啟動`OpenOCD`連線本地`JTAG`裝置了,啟動命令如下: ```shell imaginemiracle@:riscv-openocd$ sudo ./src/openocd -s ./tcl -f ./tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f ./tcl/target/riscv64_IM.cfg [sudo] password for imaginemiracle: Open On-Chip Debugger 0.10.0+dev-01145-gb7bd3f8d4 (2021-01-12-17:54) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html sw_reset_halt Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections Info : auto-selecting first available session transport "jtag". To override use 'transport select '. 0 Info : clock speed 1000 kHz Info : TAP riscv.cpu has invalid IDCODE (0xfffffffe) Info : datacount=2 progbufsize=16 Info : Disabling abstract command reads from CSRs. Info : Examined RISC-V core; found 4 harts Info : hart 0: XLEN=64, misa=0x800000000014112d Info : hart 1: currently disabled Info : hart 2: currently disabled Info : hart 3: currently disabled Info : Listening on port 3333 for gdb connections ``` 連線到本地: ```shell imaginemiracle@:riscv-openocd$ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Open On-Chip Debugger > ``` 如上進入`openocd`的命令列則說明`OpenOCD+JTAG`的整個軟體環境搭建完成。 ### 4.3、OpenOCD的一些簡單命令 `OpenOCD`常用命令 ```shell halt -暫停CPU reset -復位目標板 resume -恢復執行 resume 0x123456 -從0x123456地址恢復執行 reg -列印register暫存器的值 load_image -燒寫二進位制檔案到指定地址 例: load_image image.bin 0x4000000 -燒寫image.bin到0x4000000 dump_image -將記憶體從地址Addr開始的Size位元組資料讀出,儲存到檔案File Name中 verify_image [bin|ihex|elf] -將檔案File Name與記憶體Addr開始的資料進行比較,格式可選,bin、ihex、elf step [Addr] -不加地址:從當前位置單步執行; 加地址:從Addr處單步執行 poll -查詢目標板當前狀態 bp [hw] -在Addr地址設定斷點,指令長度為Length,hw代表硬體斷點 rbp -刪除Addr處的斷點 mdw [Count] -顯示從實體地址Addr開始的Count(預設則預設為1)個字(4Bytes) mdh [Count] -顯示從實體地址Addr開始的Count(預設則預設為1)個半字(2Bytes) mdb [Count] -顯示從實體地址Addr開始的Count(預設則預設為1)個位元組(1Byte) mww -向實體地址Addr寫入Value,大小:一個字(4Bytes) mwh -向實體地址Addr寫入Value,大小:一個半字(2Bytes) mwb -向實體地址Addr寫入Value,大小:一個位元組(1Byte