Tips and tricks
Some random tips and tricks for using ZY1000 and JTAG Debuggers generally.
Problems with starting a CDT debug sessionIf CDT debugging works on the first try, great!
Embedded debugging can be quite complex with unique quirks that have to be understood and dealt with for each setup. No two setups are alike!
Otherwise CDT is the wrong place to try to debug the GDB + hardware debugger setup as it does not yield intuitive and detailed feedback in response to each careful step in setting up debug chain for embedded systems.
The sequence below just a suggestion on how to debug, there are of course many other ways to do so.
By only advancing to the next level when each level of problems are solved, you get better feedback on problems.
- First check cables & voltages, etc.
- Start with ZY1000 telnet. The next stage is the command line version of GDB. If you are having trouble figuring out what is happening, set "debug_level 3" in your ZY1000 config script and look in the log.
- Issue then a "reset run". This will should be able to enumerate the JTAG toolchain.
- Issue a "reset init". This will reset and halt the target, then run commands to initialize the target. That is to set up the memory map, and basic poeripherals. If you only reset and halt the target "reset halt", you will probably not be able to issue a GDB load or program flash.
- Verify that you can read memory (help mdw) and modify ram (help mww).
- Test the flash if necessary. "reset init" + "flash erase" and fill the flash with some data(using flash write_image or flash fill). Type help flash.
- If you have a flash configured, you can program flash by issuing a GDB load, provided that the address of the .elf image is that of the flash chip. If you have an .elf image that is copied from flash to RAM upon startup, you'll need to add an offset to the GDB load command. Type help load in GDB.
- At this point you are ready to connect to ZY1000 using the GDB command line version.
- Next there is the issue of breakpoints. GDB will use hardware breakpoints for the memory regions set up as read only. Type "info mem" in the GDB console.
- If you have not set up flash regions in your reset init script, then GDB will use software breakpoints by default, which will fail for flash regions. The solution is to add a "gdb_override_breakpoint" command in your reset init script to force hardware breakpoints.
- At this point you should try to single step from GDB command line version.
- Next try setting a breakpoint and issuing a "continue" command and verify that the CPU breaks as expected at the breakpoint site.
- At this point you are ready to try to set up a debug session in Zylin Embedded CDT.
GDB init sequenceIf you have a working ZY1000 reset init sequence, then the GDB script should be quite straightforward:
# Connect to ZY1000 A question that might occur to you: what belongs in the reset init script and what belongs in GDB init script? The answer to this is to a degree a matter of preference. Perhaps one thing to keep in mind is that scripts executing on the ZY1000 will execute faster than a long sequence of monitor commands.
target remote 10.0.0.69:3333
# reset target and run initialisation script.
# all commands to ZY1000 are prefixed with "monitor".
monitor reset init
# load application into RAM
# If the target configuration script has been set up with flash
# and the address of the image is in flash, then ZY1000 will program
# the application into flash, erasing any data in the sectors
# where the application is to be programmed.
# set program counter to start of application.
# We use ZY1000 to do this because it is more robust
# than trying to do so with GDB. GDB can often be quite
# confused when trying to evaluate stack frames outside
# C code.
monitor reg pc 0x20000000
# To sync things up, we step a single instruction
Speeding up GDB loadOne way to speed up GDB load significantly is to upload the executable via http before the GDB load session is launched.
Typically this would be done by adding a command to post the stripped executable to ZY1000 at the end of the build file. The executable then only has to be uploaded over TCP/IP once every time it changes. Also, there will normally be enough time between completion of build and launching a debug session that the executable can be uploaded.
Add the following to the end of your build file:
curl --form email@example.com 10.0.0.69/ram/cgi/fastload.tcl
Replace "load" in your GDB init file with:
# load image from RAM disk on ZY1000
GDB low level debuggingWhen debugging low level or early startup code, it might be a good idea *NOT* to use a graphical GDB GUI until you have stepped into the C source code.
If you use the command line version of GDB, you have a much more precise control over what commands that are sent to the target. A GUI can, to be helpful, start reading variables on the stack, etc. When debugging code that is setting up the stack, then obviously that is the last thing you want.
GDB embedded vs. PC application debuggingThere are some important differences to keep in mind when debugging embedded applications using GDB vs. debugging PC applications.
For embedded, GDB does not launch the application, nor does GDB necessarily even load the application. GDB attaches to the target. The target can be in any state: powered down, running, halted. Upon attaching to the target GDB will issue a halt, which may or may not succeed, after which GDB will enter the halted state.
- Tricky! even if GDB is halted, that does not mean that the target is halted. You can use GDB "monitor" commands to e.g. reset the target into the halted state.
- Tricky #2! this means that you have to be somewhat skeptial of the CPU register info GDB reports immediately after attaching to the target, since GDB may be out of sync with the target. Use "monitor reg XX" to read the actual value of a register. "monitor reg XX" will return an error message if the target is in the running state or otherwise unresponsive.
For an application running out of flash, you would typically specify the executable on the GDB command line and the executable would only be used by GDB to read out symbols. The actual application would be programmed in flash by a JTAG Debugger beforehand or the ZY1000 can also redirect the GDB "load" to a flash programming command(including erasing any existing application).
The difference between loading symbols and loading applications is illustrated by the GDB "load" vs. "symbol" command. The former loads the application AND symbols, whereas the latter only loads the symbols. It is possible to load multiple symbol files into GDB at the same time, e.g. bootloader + application.
Target configuration scriptBefore you can use the ZY1000 to debug your board or program flash, you need a target configuration script.
The purpose of the target configuration script is to specify to ZY1000 what target is connected and how to set up a minimal configuration for that target that will enable uploading code to program flash, upload code to RAM and debug the target.
To get a target script right can be anything from trivial to quite difficult. This is because there can be a high level of necessary complexity to setting up the target. The good news is that once done, you'll probably never have to touch that script again.
ZY1000 comes with a lot of target configuration scripts that can be selected from a dropdown menu. You should start with a target script that closely resembles what you need and modify it to suit your purposes.
That tricky init sequenceIf you don't know how write an init sequence for your target, then there are a couple of resources that you can resort to:
- Do you have source code(machine code probably) to set up the target? You can try translating that machine code to a init sequence. Most often this will boil down to a dozen or two of "mww" commands in the reset event sequence. If this can be done easily, then it is probably the preferable way of doing things.
- Do you have an application binary that sets up the RAM that you can program to flash? If you are able to program that application to flash, then you can use "resume" and "halt" in your reset init sequence. This will let the CPU run for a short time, enough to set up RAM. Afterwards, you should be able to upload RAM using e.g. GDB load. Note that making a minimal reset init script that sets up flash can be a LOT easier than setting up the RAM.
- Hit the datasheets. If you read through the datasheets you should find all the information required to set up your target buried in there. This can be quite a time consuming process and it is fairly likely that someone has done this for you.
- A bit more esotheric approach: if you have internal memory for your CPU, then you may be able to use the binaries of an application to set things up. The early startup code for an application is almost certainly position independent. This should allow you to load the beginning of that application into the internal RAM and execute the first few instructions. You'll have do a bit of manual disassembly to determine how much of the beginning of the application you need. Use objcopy -O binary to create a binary file and truncate that binary file.