Debugging A
Boot Loader (U-Boot) with JTAG Emulator/JTAG FLASH Programmer and JTAG Debugger
JTAG Debuggers and JTAG FLASH Programmers are the perfect tool for bringing up a boot loader on your new hardware. A boot agent typically is
small piece
software that helps get your system going at power-up or
reset.
Despite its relative size, it is a crucial
part of your system and can create difficulties if not operating
properly. This section illustrates how a Debugger can
be used to debug a
boot agent. Although our examples below use PPC-Boot
(U-Boot) to illustrate how to debug a boot agent, the general principle
and
steps apply to other boot agents and target architectures.
General Principles
One of the issues you need
to be aware of is
that a boot agent (U-Boot in this case) starts running in
FLASH then
copies itself to RAM. For most source-level debuggers this
presents
some difficulties to the debug process: symbol relocation and software
breakpoints.
Symbol relocation
Some
symbol addresses change after the boot agent starts running from
RAM.
The result may be a function breakpoint failing if the function runs
after the copy to RAM. The problem is
further complicated if a function has FLASH and RAM
components. (board_init_f,
board_init_r).
A classic method for solving
the symbol
relocation issue would be to relink the boot image at the known
RAM relocation address. In this case, you either
load symbols from the FLASH-based image or symbols
from the RAM-based image depending on what part of the sequence you are
trying
to debug. This can be moderately challenging if you have to change the
code
often or if the RAM relocation address changes often. For PPC-Boot, the
relocation address usually stays the same for a particular build.
To better address this
issue, Agile-DB lets you load multiple symbol images while
specifying new load addresses. So, you can have both sets of symbols on
hand as
you debug the initialization.
Breakpoints
Using
software breakpoints during initialization can be problematic. First,
you
cannot use software breakpoints in FLASH because the
instruction used to generate the
breakpoint exception cannot be copied into a read-only FLASH location.
The
problem of using software breakpoints in RAM is more subtle
for 2 reasons:
- At board reset, the
RAM location may not exist in the memory map. For example, the
init code may need to enable chip selects and/or enable MMU mapping. In
fact trying to set a software breakpoint might generate an illegal
address exception that prevents the target from running cleanly.
- If the RAM does
exist and the emulator successfully writes the exception instruction to
RAM, the instruction gets overwritten when the agent copies itself out
of FLASH. A solution is to use hardware breakpoints when
trying to debug your boot sequence. After the boot agent is copied to
RAM, software breakpoints will be safe to use (and more plentiful).
Setting Up the
Debug Environment
The paragraphs and sections
below make some
assumptions regarding the debug environment.
This section describes a typical setup used to debug a boot
loader. Your environment should include the following
basic elements:
1.
Linux or
Windows host connected to the emulator via
Ethernet. (Confirm connection via
telnet). Be sure to close the telnet session when you are done.
2. The
emulator is connected to the target
connection. Verify the emulator is
properly configured and connected to the target. Open a
telnet connection and issue the st
command. If the emulator reports no error, you can proceed. If
you do see an error, or
cannot connect please refer to the “Installation” section of this
manual. Typically problems and resolutions are:
a.
If
the emulator reports “ERROR
not target found”, there is either a
power or connection issue. Check that
all cables are properly connected and oriented.
b.
Connect
a serial cable between the target
console port and a serial port on your debug host. Your host
should include terminal emulation
software such as Minicom or Tera Term to act as the console window on
your debug
host. The console window lets you see
debug messages and type commands as needed.
c.
If
you plan to remotely load Linux from PPC-Boot as
a next step, connect an
Ethernet cable between the target and the TFTP server where
the Linux image resides.
Building the Agent
for
Debugging
We recommend that you enable
some or all of
the DEBUG console output for your boot agent so that you can see the
RAM address where the code is copied. In PPC-Boot,
this is done in
/lib_ppc/board.c:
#ifdef DEBUG
printf (”Now
running in RAM – PPC-Boot at:
%08lx\n", dest_addr);
#endif
You may either define DEBUG
globally for your
build or remove the conditional for this one line. The console displays
a
message similar to the following:
>Stack Pointer at:
0ffaef48
>New Stack Pointer
is: 0ffaef48
>Now running in RAM
– PPC-Boot at:
0ffd0000 FLASH: 512 kB
>PCI Scan: Found Bus
0, Device 0, Function
0
Hardware Breakpoint
Registers
Emulators implement hardware
breakpoints
through the use of on-chip debug registers.
Unless you have specific application needs for these registers, you
want
to be sure that the boot agent code does not set or clear these
registers. Typically, the debug registers are used as
part of a resident debug monitor like Bedbug.
In some cases, the initialization code from the boot agent might clear
the registers. A symptom of the boot
agent code clearing debug registers is failing to hit expected hardware
breakpoints when running from reset.
We recommend removing the
debug monitor from
the boot agent. In the case of PPC-Boot,
remove Bedbug from the build by modifying the board-specific
configuration
file(e.g. /include/WALNUT405.h) as follows:
#define CONFIG_COMMANDS
(CONFIG_CMD_DFL | \ CFG_CMD_PCI | \
CFG_CMD_IRQ | \
CFG_CMD_I2C | \
CFG_CMD_KGDB | \
CFG_CMD_DHCP | \
CFG_CMD_DATE | \
CFG_CMD_BEDBUG | \
<- Remove this line to
remove Bedbug
CFG_CMD_ELF )
You may also want to check
the initialization
code for any references to the debug registers. For instance, the
PowerPC 440
version of PPC-Boot clears the debug registers, so you may want to
comment out
these instructions in the file start.S:
/******************************************************************/
_start_440:
/*--------------------------------------------------------*/
/* Clear and set up some
registers. */
/*--------------------------------------------------------*/
iccci r0,r0 /* NOTE: operands
not used for 440 / dccci r0,r0 /
NOTE: operands not used for 440 */
sync
li r0,0
mtspr srr0,r0
mtspr srr1,r0
mtspr csrr0,r0
mtspr csrr1,r0
/*--------------------------------------------------------*/
/* Initialize debug */
/*--------------------------------------------------------*/
/* start commented out for
emulator use of HW debug resources
mtspr dbcr0,r0
mtspr dbcr1,r0
mtspr dbcr2,r0
mtspr iac1,r0
mtspr iac2,r0
mtspr iac3,r0
mtspr dac1,r0
mtspr dac2,r0
mtspr dvc1,r0
mtspr dvc2,r0
mfspr r1,dbsr
mtspr dbsr,r1
end commented out for
emulator use of HW debug resources */
Odd Step Behavior
By default, the GNU compiler
MAKE is
configured to optimize instruction scheduling to improve execution
performance. This can result in peculiar
debugger single step behavior making debugging more
difficult. To resolve this problem, add the following
compile options in your MAKE script:
-fno-schedule-insns
-fno-schedule-insns2
This suppresses these
performance
optimizations insuring that the target behavior presented by
Agile-DB is actually what the target is doing.
Configuring Agile-DB
As indicated earlier, boot
code often is
burned in FLASH and on start up is loaded to RAM.
This section discusses how to configure and
use Agile-DB in this situation.
Since we want both the
FLASH and RAM version of the symbols and have no need
to
load code, we want to program Agile-DB to load only
symbols. You may create a new configuration (refer to
the “Create Your First Configuration File” section
in this
chapter) or modify an existing configuration.
If you use the wizard to create a new configuration, be certain to
select the Syms Only radio button in the File Settings dialog.
To modify an existing
configuration, point to
the File menu, select Create or Edit Configuration and click on Edit a
configuration. The following dialog
appears:
Make the necessary changes
to the
configuration as indicated in the figure above.
Once you have made the necessary edits, click OK and save the
configuration.
Restart
Agile-DB and select the configuration you created or
edited. The Source window should display
the beginning of the boot code’s source.
Reset the
target
Use the RESET button to
reset the target.
Depending on the target, you may or may not see valid
disassembly. For instance, IBM PPC4xx targets reset to
location 0xFFFFFFFC. In this situation
you can single step once to get the start of boot code. For
PPC-Boot, this location would be in the
file start.S. If your source paths are correct, and you have compiled
with
debug on (-g option), you should see the assembly source file with
comments in
the Agile-DB source window.
If you are trying to set
breakpoints in the
source view of start.S, note that much of the code is #if
defined out of the actual build, so
Agile-DB might complain that it cannot resolve a line
number. Be sure that you are setting the
break at a point that was compiled for your target.
Single Stepping
You can step in assembly or
C
source through the boot agent without any problem. You can also view
your code in assembly mode, Source mode or in "mixed" C and Assembley
mode for maximun detail.
Setting a Breakpoint
at a
Function in FLASH
There are a number of ways
to set breakpoints
in Agile-DB. In the example below we use the function
window to find a function and set a hardware breakpoint at the
beginning of the
function.
Open the File-Function List
window by
selecting Display
File-Function List in the Display menu
or by clicking the Show function window button in the main
toolbar. In the Name Filter text box,
type the “board” followed by return.
Agile-DB searches the available sources for functions
that include “board” in their name and lists those
functions in the
File-Function List window. The file board_init_f
should be listed.
Double-click on the file name and observe
that the source window displays the entry source for this
function. Note that board_init has 2 versions – the “_f” version that
runs from
FLASH, and the “_r” version
that runs from RAM.
In the Source window, right
click in the
margin at the opening brace (“{“) of the function
and select Set/Delete
HW breakpoint here. A red dot
appears in the margin indicating a set breakpoint.
Figure 42: Agile-DB Set Breakpoint Here
NOTE:
On some targets (PPC4xx, for instance), if you do not step at least
once
after the RESET, target flash may not be initialized. If you
double-click to browse to the
function, the memory access may cause a bus error, and the target will
not run
properly. A safe way to set the
breakpoint from reset is to right click on the function name in the
function
list, and select Set
HW
breakpoint after function entry from the RMB
menu.
Click the GO button to start
the
target. After the target halts at the breakpoint,
double-click the breakpoint symbol clear the breakpoint.
Start
the target again and note the message
from the board’s console that specifies the
RAM relocation
address for the boot agent. After this address is reported,
stop
the
target.
Setting a Breakpoint
at a
Function in RAM
This section discusses how
to use the
information we received from the steps described above to set
breakpoints in
the RAM version of the boot code.
RESET the target to get back
at the start of
boot code.
From the File menu, select Add Symbol File and then browse to select the
PPC-Boot ELF image once again. A dialog appears that
allows you to specify
relocation addresses for up to 6 sections.
For our example, we only need to enter the text section address that
was
reported in the previous step. Enter the
address then click OK. Dismiss the
status dialog that appears.
Display the File-Function
List window and
type the “board” again in the Name
Filter.
Notice this time there are two instances of all the names but the
address lets you distinguish the RAM versus
FLASH version. Point to the RAM version of board_init_r
and select Run
to this function (HW BP) from the right mouse
button menu. The target runs to this
point and stops.
If RAM is not yet
enabled, do not try to browse to
functions in RAM. This type of access
may generate a bus error and force you to reset the target.
Use the right mouse menu in the Function-File
List window to set a breakpoint if you are not sure.
.
At this point, you can
select points of
interest in the Source window and simply double-click a line of code to
set
software breakpoints. You can also set
software breakpoints in any RAM-based functions listed
in the File-Function List window.
Adding Symbols as
Part of the
Configuration
You can avoid repeating the
above steps, if
the RAM relocation address does not change every time
the target is run. This is accomplished
by adding the Add Symbol File operation to the configuration.
Prior to ending the current session or prior
to opening this configuration next, edit the configuration as follows:
- Select Create or Edit
Configuration submenu from the File menu.
- Click Edit a
Configuration, select the configuration file to be edited and click OK.
- In the Properties dialog,
double-click the After Load Command to bring up Editing After Load
Command dialog.
- Click Add and type the
command:
- addsym PPC-Boot.elf
<address>
- where:
- PPC-Boot.elf
is the name of your PPC-Boot ELF image file
- address is a
numerical value representing the relocation address you expect.
- Close the dialogs and
save your configuration to the same or a different file.
- The next time the
configuration is opened both the FLASH and
RAM version of the symbol table is loaded.
|