📜 ⬆️ ⬇️

OpenOCD, GDB and (strongly) remote debugging

It is given: there is a device, with ARM926E-JS (Cypress FX3) on board. The device is located on another continent. The device is connected (JTAG + USB + COM) to a Linux computer. On the computer there is SSH access (and nothing more, only the SSH port).

Problem: The device needs to be debugged and code written under it. And do it, preferably, conveniently.

Solution using OpenOCD, GDB and Qt Creator, as well as describing the path to it, under the cat.

Solutions to the problem can be many. The fastest and simplest: launching a GDB + OpenOCD bundle on a remote computer via an ssh session. Convenience is not much, because it is more convenient to edit the code locally, and for debugging you need to constantly upload the code to the server using scp or rsync.
')
After a brief reflection, the idea comes: after all, we can run commands remotely on the server using SSH:
ssh user@host some-command some-arguments 

Hmm ... And besides, GDB can start OpenOCD itself in pipeline mode and communicate with it. So you can also make it so that not just OpenOCD is launched, but remote, via ssh, and the resulting bundle has already been used for debugging.

Unfortunately, this option was not viable: the connection was constantly falling off on timeout.

The next idea was to somehow raise the VPN and use it to connect to any ports on the server, and then start OpenOCD remotely.

But how to raise VPN if there are no ports other than SSH? Well, we know that SSH can forward ports. Launching remotely OpenOCD, forwarding the port ... Yes, a little better than starting in pipeline mode. But precisely that a bit. For work does not suit.

I almost decided to quit this business and use the very first, simple, reliable, but inconvenient solution, but then I typed a bunch in Google: SSH VPN. To say that I was surprised - to say nothing. In order not to look, these two links are enough:

After the settings, the remote machine has the tap8 interface with the address 192.168.100.1 and locally: the tap7 interface with the address 192.168.100.2 (the addresses will be useful later).

I try to run ... lo and behold! The solution turned out to be a worker! The code is loaded, everything works, breakpoints are set. Problem one: slowly. And if I can accept the expectation of updating the state (spectra, local variables, etc.), then the load of 300 kB of elf takes more than 6 minutes. Locally faster. Much.

In any case, here are a couple of scripts that implement this scheme (I do not provide SSH settings):
  1. openocd-remote is just a shell for launching a remote OpenOCD via ssh. I will note that the location
    I made the files and directories on the local machine and the remote one the same. Otherwise the case
    In the same script, I would add preprocessing of parameters with sed to make replacements. A plus,
    OpenOCD is compiled from Git and copied to ~/bin/openocd-git/{bin,share}
    ~/bin/openocd-git/{bin,share}
    (corresponding directories).
    Configs for FX3 (about them further) lie in ~/bin/openocd-git/
    ~/bin/openocd-git/
    . In ~/bin/
    ~/bin/
    a symlink is made on the executable
    openocd file
     #!/bin/sh ssh user@host -T killall -9 openocd exec ssh -TC user@host bin/openocd $@ 
  2. gdb-remote - connects to a remote OpenOCD, loads the code:
     #!/bin/sh gdbcfg=fx3_gdb.ini elf=some-code.elf cat > $gdbcfg << EOF set prompt (arm-gdb) set remotetimeout 30 target remote 192.168.100.1:3333 monitor halt monitor soft_reset_halt monitor adapter_khz 1000 set endian little load EOF arm-none-eabi-gdb -x $gdbcfg $elf 

Thoughts on speeding up the launch went something like this: simply copying an elf file to a remote server takes 10 seconds, plus or minus. But it would be cool to upload an image to the server and upload to the device from it already ...

Reviewing the OpenOCD documentation and here it is: OpenOCD itself can load the code into the device, and GDB will simply connect and give the command to start the firmware. Magic command: load_image .

The first experiments were disappointing: the download is VERY unstable. The code is loaded, loaded quickly: 1 minute versus 6 with a tail). But the firmware starts, then no. At the same time, if in the same GDB session to do a load , then everything starts fine.

Started looking for differences. I was interested in the last line of loading through load :
 Start address 0x40035948, load size 298456 

This inspired to log in after loading the code via load and via load_image (via OpenOCD) and before starting ( continue ) the contents of the $pc register. And ... the difference is found: after load $pc installed exactly at this “Start address”, whereas after load_image in $pc at the moment of which the download was started. After installing pc to the correct value, the load became stable. The question remains: magic numbers are not good. But it helped that in GDB you can specify a character and its address will be taken. In the case of FX3, this symbol: CyU3PFirmwareEntry (by the way, on local applications it will most likely be _start ) and the $pc setup command has turned into this:
 set $pc = CyU3PFirmwareEntry 
In addition, GDB has the ability to call shell commands, so we can easily and naturally, at startup, upload the elf file to a remote server and give the command to the running OpenOCD to load it (any command for OpenOCD can be given from GDB by preceding it with the word monitor ).

The final script for running GDB is:
 #!/bin/sh gdbcfg=fx3_gdb.ini elf=some-code.elf #    GDB cat > $gdbcfg << EOF set prompt (arm-gdb) set remotetimeout 30 target remote 192.168.100.1:3333 shell scp $elf user@192.168.100.1: monitor halt monitor soft_reset_halt monitor sleep 1000 monitor load_image %elf 0x00 elf set $pc = CyU3PFirmwareEntry EOF arm-none-eabi-gdb -x $gdbcfg $elf 
The script for running OpenOCD remains the same.

What we need now to start remote debugging:
  1. Run the openocd-remote script. You can restart it according to your needs.
  2. Spread the code and run the gdb script above.
  3. ...
  4. PROFIT
And PROFIT? For me, not so. I write the code in Qt Creator and I want to do all this from it in one click. And this is done in one click. Enough:
  1. Open the settings dialog
  2. Select Bare Metal and add a new GDB Server Provider of the OpenOCD type with the following parameters:
    • Name: at your discretion, let it be FX3 Remote
    • Startup mode: Startup in TCP / IP
    • Host: 192.168.100.1
    • Port: 3333
    • Executable file: path to openocd-remote, I have this / home / alexd / bin / openocd-remote
    • Root scripts directory: / home / alexd / bin / openocd-git / share / openocd / scripts - yours may differ,
      most importantly, remember that a smart tuner checks these directories for accessibility, which is why I
      did the same tree on the local computer and on the remote one.
    • Configuration file: /home/alexd/bin/openocd-git/share/openocd/scripts/interface/ftdi/olimex-arm-usb-ocd-h.cfg - at
      I’m using the Olimex ARM-USB-OCD-H debugger, you may have another one. Debugger configuration is not considered.
    • Additional argumets: -f ~ / bin / openocd-git / fx3-common.cfg -f ~ / bin / openocd-git / fx3-threadx.cfg - these scripts
      I will post below.
    • Init commands - the most interesting:
       monitor echo "Upload image..." shell scp %{DebuggedExecutable:NativeFilePath} user@192.168.100.1: monitor halt monitor soft_reset_halt monitor sleep 1000 monitor echo "Load image..." monitor load_image %{DebuggedExecutable:FileName} 0x00 elf set $pc = CyU3PFirmwareEntry monitor echo "Run image..." 
      As you can see, completely repeats the code from the script, only with macro substitutions, so as not to rewrite for each target.
    • Reset commands: monitor reset halt (leave by default).
  3. Then we go to Devices, add -> Bare Metal Device -> Give the name (let it be FX3 Device Remote) and assign our GDB Server provider (FX3 Remote)
  4. Then we go to Build & Runs and in the used set (Kits) for Cypress (or ARM or whatever you have there) choose:
    • Device type: Bare Metal Device
    • Device: FX3 Device Remote
Then I had to create for a set - for local and for remote development. But well ... you can survive.

Everything, after which we add a new set in the project settings, configure it, on the Run tab we add the configurations in the name of which is "(via GDB Server or hardware debugger)" and start debugging simply by pressing F5.

Useful materials


Scripts fx3-common.cfg, fx3-threadx.cfg, fx3-boot.cfg (for debugging the bootloader or when there is no ThreadX), respectively:
fx3-common.cfg
 ###################################### # Target: CYPRESS FX3 ARM926-ejs # Common part ###################################### if { [info exists CHIPNAME] } { set _CHIPNAME $CHIPNAME } else { set _CHIPNAME fx3 } if { [info exists ENDIAN] } { set _ENDIAN $ENDIAN } else { set _ENDIAN little } if { [info exists CPUTAPID] } { set _CPUTAPID $CPUTAPID } else { set _CPUTAPID 0x07926069 } #delays on reset lines adapter_nsrst_delay 200 jtag_ntrst_delay 200 adapter_khz 1000 #reset_config trst_only #reset_config trst_only combined #reset_config trst_and_srst combined #reset_config trst_and_srst srst_pulls_trst # From the Cypress SDK reset_config trst_and_srst srst_pulls_trst # My own well worked #reset_config trst_only jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID jtag_rclk 3 


fx3-threadx.cfg
 ###################################### # Target: CYPRESS FX3 ARM926-ejs ###################################### #source [find fx3-common.inc] ###################### # Target configuration ###################### set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME arm926ejs -endian $_ENDIAN -chain-position $_TARGETNAME -rtos ThreadX adapter_khz 1000 


fx3-boot.cfg
 ###################################### # Target: CYPRESS FX3 ARM926-ejs ###################################### #source [find fx3-common.inc] ###################### # Target configuration ###################### set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME arm926ejs -endian $_ENDIAN -chain-position $_TARGETNAME adapter_khz 1000 



The project type in Qt Creator for ARM, FX3 and others like them can be Generic, but I wrote CMake rules for FX3: github.com/h4tr3d/fx3-cmake and use CMake Project manager, which makes it easy to have several configurations in different directories, shadow assembly and a reason not to be confused in the assembly parameters on complex projects.

OpenOCD commands: openocd.org/doc/html/General-Commands.html

To calculate the Entry Point automatically, you can build GDB with python support and use the recommendations:

Source: https://habr.com/ru/post/274179/


All Articles