zoukankan      html  css  js  c++  java
  • 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官网下载源码,已经有很多平台提供配套目标平台的OpenOCD源码,也可以通过这些来下载带有相应目标平台配置文件的OpenOCD,本文主要介绍支持riscv64OpenOCD下载和使用。
    riscv-openocd下载链接: https://github.com/sifive/riscv-openocd
    通过git下载:

    git clone https://github.com/sifive/riscv-openocd.git
    

    3、编译与安装

    <1> 进入riscv-openocd源码目录

    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支持的调试器的类型,笔者使用的JTAGFTDI类型,因此需要OpenOCD支持FTDI
    [注] 通过查看JTAG手册了解所使用的JTAG是什么类型,通过./configure --help 命令查看所需要开启的类型以及其它配置参数

    #创建安装目录
    imaginemiracle@:riscv-openocd$ mkdir install_IM
    #执行bootstrap
    imaginemiracle@:riscv-openocd$ ./bootstrap
    

    bootstrap执行的正常输出:

    + 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文件

    #配置安装目录路径必须为绝对路径
    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编译并安装

    #编译后并且安装
    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=]

    #错误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 '
    ':
          |    ^~~~
    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语句中,因此在这里直接忽略该错误继续编译即可。

    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]

    #错误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: 按照道理说这种写法是没什么问题的,但还是会报错,因此将报错的三个位置进行修改,修改后的代码如下。

    imaginemiracle@:openocd$ vim src/target/arm_disassembler.c +1499
    
    //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 "	0x%8.8" PRIx32 "	SMLAW%s%s r%i, r%i, r%i, r%i",
                        address,
                        opcode,
                        (y) ? "T" : "B",
                        COND(opcode),
                        Rd,
                        Rm,
                        Rs,
                        Rn);
            }
    
            /* SMUL < x><y> */
    //============================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 "	0x%8.8" PRIx32 "	SMULW%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 "	0x%8.8" PRIx32 "	SMULW%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=]

    #错误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,格式溢出,一看就是不重要的错误,直接忽略就好继续编译。

    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 <sys/sysctl.h> header is deprecated and will be removed." [-Werror=cpp]

    #错误4
    In file included from src/helper/options.c:38:
    /usr/include/x86_64-linux-gnu/sys/sysctl.h:21:2: error: #warning "The <sys/sysctl.h> header is deprecated and will be removed." [-Werror=cpp]
       21 | #warning "The <sys/sysctl.h> header is deprecated and will be removed."
          |  ^~~~~~~
    cc1: all warnings being treated as errors
    

    解决方案 4: 可以看出这里是使用了Linux已经移除的头文件<sys/sysctl.h>报错的,因此进入报错文件,做以下修改。

    imaginemiracle@:openocd$ vim src/helper/options.c +38
    
    //File src/helper/options.c +38
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include "configuration.h"
    #include "log.h"
    #include "command.h"
    
    #include <getopt.h>
    
    #include <limits.h>
    #include <stdlib.h>
    #if IS_DARWIN
    #include <libproc.h>
    #endif
    //===================Alter by me===================
    #ifdef HAVE_SYS_SYSCTL_H
    //#include <sys/sysctl.h>
    #endif
    #if IS_WIN32 && !IS_CYGWIN
    #include <windows.h>
    #endif
    //=================== End  Alter ===================
    static int help_flag, version_flag;
    

    4、OpenOCD的使用

    4.1、OpenOCD的配置

    安装成功后则会在配置的安装目录里生成如下文件,这里的openocd就是需要用到的可执行程序将它拷贝到需要执行的目录,或者直接在当前目录使用也可以。([注]: 若未配置安装路径,默认安装到“./src目录”中)

    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厂商都会提供购买到的JTAGopenocd配置文件,这里就可以直接使用。

    <1> JTAG设备配置文件

    前文提到笔者所使用的JTAG型号为Olimex-ARM-USB-TINY-H,其配置文件如下:

    # 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设备,则说明驱动安装成功。

    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设备的VIDPID,这里的值应该与JTAG配置文件中的vid_pid相同才可。

    <2> 目标平台配置文件

    OpenOCD的配置文件使用的是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设备了,启动命令如下:

    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 <transport>'.
    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
    

    连接到本地:

    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常用命令

    halt	-暂停CPU
    reset	-复位目标板
    resume 	-恢复运行
    resume 0x123456   -从0x123456地址恢复运行
    reg <register>    -打印register寄存器的值
    
    load_image <File Name> <Addr>		    -烧写二进制文件到指定地址
    例: load_image image.bin 0x4000000  	-烧写image.bin到0x4000000
    
    dump_image <File Name> <Addr> <Size>    -将内存从地址Addr开始的Size字节数据读出,保存到文件File Name中
    
    verify_image <File Name> <Addr> [bin|ihex|elf] 	-将文件File Name与内存Addr开始的数据进行比较,格式可选,bin、ihex、elf
    
    step [Addr]		-不加地址:从当前位置单步执行; 加地址:从Addr处单步执行
    poll		    -查询目标板当前状态
    bp <Addr> <Length> [hw] 	-在Addr地址设置断点,指令长度为Length,hw代表硬件断点
    rbp <Addr>		 -删除Addr处的断点
    
    mdw <Addr> [Count]	 -显示从物理地址Addr开始的Count(缺省则默认为1)个字(4Bytes)
    mdh <Addr> [Count]	 -显示从物理地址Addr开始的Count(缺省则默认为1)个半字(2Bytes)
    mdb <Addr> [Count]	 -显示从物理地址Addr开始的Count(缺省则默认为1)个字节(1Byte)
    mww <Addr> <Value>   -向物理地址Addr写入Value,大小:一个字(4Bytes)
    mwh <Addr> <Value>   -向物理地址Addr写入Value,大小:一个半字(2Bytes)
    mwb <Addr> <Value>   -向物理地址Addr写入Value,大小:一个字节(1Bytes)
    
  • 相关阅读:
    Python使用print打印时,展示内容不换行
    二进制、八进制、十进制和十六进制的相互转化(图解)
    IDEA 配置背景颜色(豆沙绿)
    Linux下脚本文件第一行的作用
    IDEA 注释模板配置
    IDE 常用配置
    Python 运算符
    Python 基础
    Chrome 浏览器快捷键
    正则表达式
  • 原文地址:https://www.cnblogs.com/ImagineMiracle-wxn/p/Riscv-OpenOCD_And_JTAG.html
Copyright © 2011-2022 走看看