AppNote-237
Understanding the bootrom image
Debugging tips for vxWorks Bootrom images
Copyright © 1984-2003 Wind River Systems Inc.
ALL RIGHTS RESERVED.
Abstract
This AppNote is intended for those people who need to get the vxWorks
bootrom or bootrom_uncmp image working. The developer has made
changes to config.h, sysLib.c, romInit.s and potentially some other files
and things build just fine, but it doesn't execute as expected. Now what?
This document will describe the files utilized by the bootrom builds, the
bootrom program flow, and some useful debugging techniques to
achieve the desired bootrom.
To confirm that the bootrom code is getting programmed into the correct
location in ROM we will briefly discuss the HEX file format.
Throughout this document the terms ROM and Flash will be used
interchangeably.
It is assumed that the hardware has a serial output device, otherwise the
vxWorks bootrom code would not be used.
This document will not explore the actual steps needed to program the
Flash that contains the bootrom.
Version 1 January 2003
Understanding the bootrom image AppNote- 237
Program Flow and Files used
Skip this section and go directly to the Debugging Techniques section if the
developer is very familiar with the files used and the program flow of the bootrom
code.
Since the bootrom is built using the command line we need to set up the
environment variables. The bootrom can be built from within the project facility but
that eventually makes a command line make request for the specified build target.
The best way to do this is to run the torVars script in the
$(WIND_BASE)/host/$(WIND_HOST_TYPE)/bin directory. It looks like this for
Tornado 2.2 on a Windows machine in a DOS window.
C :\ T22 \ppc\ host\ x86-win32 \bin>torVars
C:\T22\ppc\host\x86-win32\bin>rem Command line build environments
C:\T22\ppc\host\x86-win32\bin>set WIND_HOST_TYPE=x86-win32
C:\T22\ppc\host\x86-win32\bin>set WIND_BASE=C:\T22\ppc
C:\T22\ppc\host\x86-win32\bin>set PATH=C:\T22\ppc\host\x86-
win32\bin;C:\WINNT\SYSTEM32;C:\WINNT;
C:\T22\ppc\host\x86-win32\bin>rem Diab Toolchain additions
C:\T22\ppc\host\x86-win32\bin>set DIABLIB=C:\T22\ppc\host\diab
C :\ T22 \ppc\ host\ x86-win32 \bin>set
PATH=C:\T22\ppc\host\diab\WIN32\bin;C:\T22\ppc\host\x86-
win32\bin;C:\WINNT\SYSTEM32;C:\WINNT;
Now we are ready to run make in the BSP directory. Below is an example we will use
for discussion. It is a bootrom build using the Tornado 2.2 wrSbc824x BSP.
C:\T22\ppc\target\config\wrSbc824x>make bootrom
Automatic dependency generation
ccppc -M -MG -w -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -Wall -I/h
-I. -IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -
IC:\T22\ppc\target/src/config -IC:\T22\ppc\target/src/drv -DCPU=PPC603 -
DTOOL_FAMILY=gnu -DTOOL=gnu eeprom.c i8250Sio.c m8240AuxClk.c m8240Epic.c
sysCacheLockLib.c sysFei82557End.c sysLib.c sysNet.c sysPci.c sysPciAutoConfig.c
sysPnic169End.c sysSerial.c sysVware.c C:\T22\ppc\target\config\all/bootConfig.c
C:\T22\ppc\target\config\all/bootInit.c C:\T22\ppc\target\config\all/dataSegPad.c
C:\T22\ppc\target\config\all/usrConfig.c C:\T22\ppc\target\config\all/version.c >
depend.wrSbc824x
ccppc -E -P -M -w -mcpu=603 -mstrict-align -E -xassembler-with-cpp -I/h -I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu romInit.s >>
depend.wrSbc824x
ccppc -E -P -M -w -mcpu=603 -mstrict-align -E -xassembler-with-cpp -I/h -I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu sysALib.s >>
depend.wrSbc824x
2
Understanding the bootrom image AppNote- 237
Clean up the dependencies file by stripping ^M and adding $WIND_BASE where
needed.
wtxtcl C:\T22\ppc/host/src/hutils/bspDepend.tcl depend.wrSbc824x
The bootInit.c file contains the romStart() function. Which is the C code entry point.
At the end of romStart() we call usrInit(). The purpose of romStart() is to get the Text
and Data in the proper locations in RAM, so that usrInit() can be executed.
ccppc -c -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -Wall -I/h-I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu
C:\T22\ppc\target\config\all\bootInit.c
The romInit.s file contains the romInit() entry point and is the first code to execute as
it is positioned at the reset vector. At the end of romInit() we call romStart(). Note
that some registers can only be written once after power on reset. These registers
might be modified in romInit(), which can sometimes cause problems if a soft reset
is performed. Make sure to modify these registers in the cold boot section of
romInit.s.
ccppc -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -I/h -I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config-
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu -P -xassembler-
with-cpp -c -o romInit.o romInit.s
The bootConfig.c file contains usrInit() and contains the flow for bringing up the
vxWorks kernel. We will discuss this in detail later. If the developer needs to change
bootConfig.c, do not modify the copy in $(WIND_BASE)/target/config/all. Instead,
copy bootConfig.c to the BSP directory and change/add the BOOTCONFIG macro in
the BSP’s Makefile. (ie. BOOTCONFIG = ./mybootConfig.c)
ccppc -c -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -Wall -I/h-I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu
C:\T22\ppc\target\config\all\bootConfig.c
The sysALib.s file will not be used, unless certain assembly level functions are
required.
ccppc -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -I/h -I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu -P -xassembler-
with-cpp -c -o sysALib.o sysALib.s
The sysLib.c file is the key file for mapping the hardware to the vxWorks kernel.
ccppc -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -Wall -I/h -I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu-c sysLib.c
3
Understanding the bootrom image AppNote- 237
The version.c file is just a simple file to create the vxWorks version information and
the creation time and date of the bootrom image for the RAM based executable.
ccppc -c -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -Wall -I/h-I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu -o version.o
C:\T22\ppc\target\config\all/version.c
Create the executable part of the bootrom that gets copied or uncompressed to
RAM_HIGH_ADRS. Make note of the -Ttext 01F00000 value as it should be equal to
RAM_HIGH_ADRS for this specific BSP.
ldppc -o tmp.o -X -N -e usrInit -Ttext 01F00000 bootConfig.o version.o sysALib.o
sysLib.o --start-group -LC:\T22\ppc\target/lib/ppc/PPC603/gnu -
LC:\T22\ppc\target/lib/ppc/PPC603/common -lcplus -lepcommon -lepdes -lgnucplus -
lsnmp -lvxcom -lvxdcom -larch -lcommoncc -ldcc -ldrv -lgcc -lnet -los -lrpc
-ltffs-lusb -lvxfusion -lvxmp -lwdb -lwind -lwindview
C:\T22\ppc\target/lib/libPPC603gnuvx.a --end-group -T
C:\T22\ppc\target/h/tool/gnu/ldscripts/link.RAM
This creates the binary file containing just text and data for the RAM based
executable.
C:\T22\ppc\host\x86-win32\bin\objcopyppc -O binary --binary-without-bss tmp.o tmp.out
Compress the RAM executable portion of the bootrom.
C:\T22\ppc\host\x86-win32\bin\deflate < tmp.out > tmp.Z
Deflation: 60.52%
Take the compressed data and create a data array in assembly.
C:\T22\ppc\host\x86-win32\bin\binToAsm tmp.Z >bootrom.Z.s
Assemble the compressed data array, binArrayStart[], to get the object code. Keep in
mind that binArrayStart[] contains the compressed image that will eventually run
from RAM_HIGH_ADRS.
ccppc -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -I/h -I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config-
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu -P -xassembler-
with-cpp -c -o bootrom.Z.o bootrom.Z.s
Once again, create the versioning information but this time for the ROM based
executable.
ccppc -c -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -Wall -I/h-I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu -o version.o
C:\T22\ppc\target\config\all/version.c
4
Understanding the bootrom image AppNote- 237
Perform the final link of the executable that goes into ROM. Notice the entry point
_romInit is located at -Ttext 00100000 or RAM_LOW_ADRS for this specific BSP.
Why not RAM_TEXT_ADRS? That will be explained later.
ldppc -X -N -e _romInit -Ttext 00100000 -o bootrom romInit.o bootInit.o version.o
bootrom.Z.o --start-group -LC:\T22\ppc\target/lib/ppc/PPC603/gnu -
LC:\T22\ppc\target/lib/ppc/PPC603/common -lcplus -lepcommon -lepdes -lgnucplus
-lsnmp -lvxcom -lvxdcom -larch -lcommoncc -ldcc -ldrv -lgcc -lnet -los -lrpc
-ltffs -lusb -lvxfusion -lvxmp -lwdb -lwind -lwindview
C:\T22\ppc\target/lib/libPPC603gnuvx.a --end-group -
TC:\T22\ppc\target/h/tool/gnu/ldscripts/link.RAM
Check to see that the size of the bootrom will fit on the flash. The size of the flash is
defined by the macro ROM_SIZE in the BSP’s Makefile and the config.h file. For this
specific BSP ROM_SIZE is defined as 512KB(0x80000).
C:\T22\ppc\host\x86-win32\bin\romsize ppc -b 00080000 bootrom
bootrom: 16784(t) + 206048(d) = 222832 (301456 unused)
That is the build for the bootrom image. If the developer were to create the
bootrom_uncmp image it would be even simpler as it is not necessary to compress
the RAM based executable. Keep in mind that the bootrom program has a fully
functional vxWorks pre-emptive priority scheduler running within it. The very same
scheduler found in the vxWorks application kernel.
A very helpful build time debugging technique is to get the post processed output
from the compiler. The developer can do this by using the following command make
ADDED_CFLAGS=-E file.o > file.i. Then remove all the lines starting with "#" and all
blank lines, as there will be many. This is a good way to see what the macro's
evaluated to and a good way to see which code is being used. There are many
conditional compiles within the bootrom source code. It is pretty messy to wade
through, but it is very flexible and it works.
Here is an example of what romStart() in bootInit.c looks like after doing the
command make ADDED_CFLAGS=-E bootInit.o > bootInit.i.
void romStart
(
register int startType
)
{
volatile
FUNCPTR absEntry;
((FUNCPTR)(((UINT) copyLongs - (UINT)romInit) + 0xFFF00100 ) ) (0xFFF00100,
(UINT)romInit,((UINT)binArrayStart - (UINT)romInit)/ sizeof (long));
((FUNCPTR)(((UINT) copyLongs - (UINT)romInit) + 0xFFF00100 ) ) ((UINT
*)((UINT)0xFFF00100 + ((UINT)(((int)( binArrayEnd ) & ~( sizeof(long) - 1)) ) -
5
Understanding the bootrom image AppNote- 237
(UINT)romInit)), (UINT *)(((int)( binArrayEnd ) & ~( sizeof(long) - 1)) ) ,
((UINT)wrs_kernel_data_end - (UINT)binArrayEnd) / sizeof (long));
if (startType & 0x02 )
{
fillLongs ((UINT *)((0x00000000 + 0x4400 ) ),((UINT)romInit - 0x1000 -
(UINT)(0x00000000 + 0x4400 ) ) / sizeof(long), 0);
fillLongs ((UINT *)wrs_kernel_data_end,((UINT)(0x00000000 + 0x04000000 -
0x02000000 ) - (UINT)wrs_kernel_data_end) / sizeof (long), 0);
*(((char *) (0x00000000 + 0x4200 )) ) = '\0' ;
}
{
if (inflate ((UCHAR *)(((UINT) binArrayStart - (UINT)romInit) + 0xFFF00100) ,
(UCHAR *)0x01F00000 , binArrayEnd - binArrayStart) != 0 )
return;
absEntry = (FUNCPTR)0x01F00000 ;
}
(absEntry) (startType);
}
Let's discuss the above section of romStart() code for a bootrom build:
The line:
((FUNCPTR)(((UINT) copyLongs - (UINT)romInit) + 0xFFF00100 ) ) (0xFFF00100,
(UINT)romInit,((UINT)binArrayStart - (UINT)romInit)/ sizeof (long));
Runs the function copyLongs() from ROM and copies the ROM code from
ROM_TEXT_ADRS up to the start of the data array(binArrayStart), which contains the
RAM executable, to RAM_LOW_ADRS where romInit() is linked to.
The next line:
((FUNCPTR)(((UINT) copyLongs - (UINT)romInit) + 0xFFF00100 ) )((UINT -
*)((UINT)0xFFF00100 + ((UINT)(((int)( binArrayEnd ) & ~(sizeof(long) - 1)) )
(UINT)romInit)), (UINT *)(((int)( binArrayEnd ) & ~( sizeof(long) - 1)) ) ,
((UINT)wrs_kernel_data_end - (UINT)binArrayEnd) / sizeof (long));
Runs the function copyLongs() from ROM and copies long aligned data from
binArrayEnd to the end of data into RAM.
Basically we just copied the image from ROM into RAM and skipped the data array
binArrayStart[] in the process. This was done to save time, since the data can be
read from ROM directly during decompression.
The "if (startType & 0x02 )" just zeroes out some RAM memory areas. It is a good
idea to double check that this is legitimate.
The next line:
if (inflate ((UCHAR *)(((UINT) binArrayStart - (UINT)romInit) + 0xFFF00100 ) ,
(UCHAR *)0x01F00000 , binArrayEnd - binArrayStart) != 0 ) return;
Is the line that does the decompression of the ROM based binArrayStart[] array
which contains the compressed RAM executable into RAM_HIGH_ADRS. If this
6
Understanding the bootrom image AppNote- 237
completes successfully we have the RAM based executable sitting at
RAM_HIGH_ADRS which is where the code was linked to.
Note that the inflate() routine is running from the RAM copy of the function not the
ROM copy. It is here that we stop executing code from ROM, but we do read the data
from the binArrayStart[] array out of ROM.
The final lines:
absEntry = (FUNCPTR)0x01F00000 ;
(absEntry) (startType);
Jump to the usrInit() function in bootConfig.c. Remember from the build output
above, usrInit() was linked at RAM_HIGH_ADRS. This is where we leave the ROM
based executable for good and are now completely running from RAM.
Now lets take a look at some of the post processed bootConfig.c file. Specifically the
usrInit(), usrRoot(), and bootCmdLoop() functions.
void usrInit
(
int startType
)
{
while (trapValue1 != 0x12348765 || trapValue2 != 0x5a5ac3c3 )
{
;
}
cacheLibInit (0x02 , 0x02 );
bzero (edata, end - edata);
sysStartType = startType;
intVecBaseSet ((FUNCPTR *) ((char *) 0x0) );
excVecInit ();
sysHwInit ();
usrKernelInit ();
cacheEnable (INSTRUCTION_CACHE);
kernelInit ((FUNCPTR) usrRoot, (24000) ,
(char *) (end) ,
sysMemTop (), (5000) , 0x0 );
}
7
Understanding the bootrom image AppNote- 237
The lines:
while (trapValue1 != 0x12348765 || trapValue2 != 0x5a5ac3c3 )
{
;
}
Are used as a sanity check to see if the data section is aligned correctly. The
comments in bootConfig.c give a good explanation of this.
We then see:
cacheLibInit (0x02 , 0x02 );
This initializes the libraries used for the cache. If a problem is suspected in the
cache, turn off caching for the bootrom. Do this by making sure #undef
INCLUDE_CACHE_SUPPORT is in effect for the BSP’s config.h file. Not a bad idea
during initial development and debugging, but expect a performance hit.
bzero (edata, end - edata);
This will zero out the BSS space. Everything from the end of data to the end of the
image will be zeroed out.
sysStartType = startType;
intVecBaseSet ((FUNCPTR *) ((char *) 0x0) );
excVecInit ();
Here we set the sysStartType global, set the interrupt vector base address and make
a call to excVecInit() which will initialize all the interrupt and exception vectors to the
vxWorks defaults. It is very unusual to see something go wrong here, unless the
memory is not writeable for the interrupt/exception vectors.
sysHwInit ();
Is called to initialize any hardware to a quiet/stable state. This function is in the
sysLib.c file within the BSP. This function should be understood and looked at very
closely as it is specific to the hardware.
usrKernelInit ();
The source to this function can be found in
$(WIND_BASE)/target/src/config/usrKernel.c and is filled with several *Init()
routines. It is very unusual to see something go wrong here.
cacheEnable (INSTRUCTION_CACHE);
Enables the instruction cache. If a problem is suspected here turn off caching.
kernelInit ((FUNCPTR) usrRoot, (24000) , (char *) (end), sysMemTop (), (5000) ,
0x0 );
This call is where the first task, usrRoot(), of the system is started. Interrupts are
possible for the first time after kernelInit() does it's work. If the system makes a call
to kernelInit() and locks up, first suspect the interrupts since they seem to be the
most likely culprit from experience. Take a close look at any unexpected interrupts
8
Understanding the bootrom image AppNote- 237
that may occur in the system. The kernelInit() function NEVER returns. Always check
the arguments to this function for legitimacy.
Now lets examine the usrRoot() task as it is used to set up the rest of the vxWorks
operating system utilities.
void usrRoot
(
char * pMemPoolStart,
unsigned memPoolSize
)
{
char tyName [20];
int ix;
int count;
END_TBL_ENTRY* pDevTbl;
memInit (pMemPoolStart, memPoolSize);
sysClkConnect ((FUNCPTR) usrClock, 0);
sysClkRateSet (60 );
sysClkEnable ();
selectInit (50 );
iosInit (20 , 50 , "/null");
consoleFd = (-1) ;
if (1 > 0)
{
ttyDrv();
for (ix = 0; ix < 1 ; ix++)
{
sprintf (tyName, "%s%d", "/tyCo/", ix);
(void) ttyDevCreate (tyName, sysSerialChanGet(ix), 512, 512);
if (ix == 0 )
{
strcpy (consoleName, tyName);
consoleFd = open (consoleName, 2 , 0);
(void) ioctl (consoleFd, 4 , 9600 );
(void) ioctl (consoleFd, 3 ,
0x01 | 0x02 | 0x04 | 0x08 );
}
}
9
Understanding the bootrom image AppNote- 237
}
ioGlobalStdSet (0 , consoleFd);
ioGlobalStdSet (1 , consoleFd);
ioGlobalStdSet (2 , consoleFd);
pipeDrv ();
excShowInit ();
excInit ();
excHookAdd ((FUNCPTR) bootExcHandler);
logInit (consoleFd, 5);
bootElfInit ();
muxMaxBinds = 8 ;
if (muxLibInit() == (-1) )
return;
for (count = 0, pDevTbl = endDevTbl; pDevTbl->endLoadFunc != ((void *)0) ;
pDevTbl++, count++)
{
cookieTbl[count].pCookie = muxDevLoad (pDevTbl->unit, pDevTbl->endLoadFunc,
pDevTbl->endLoadString,
pDevTbl->endLoan, pDevTbl->pBSP);
if (cookieTbl[count].pCookie == ((void *)0) )
{
printf ("muxLoad failed!\n");
}
cookieTbl[count].unitNo=pDevTbl->unit;
bzero((void *)cookieTbl[count].devName,8 );
pDevTbl->endLoadFunc((char*)cookieTbl[count].devName, ((void *)0) );
}
taskSpawn ("tBoot", bootCmdTaskPriority, bootCmdTaskOptions,
bootCmdTaskStackSize, (FUNCPTR) bootCmdLoop,
0,0,0,0,0,0,0,0,0,0);
}
The line:
memInit (pMemPoolStart, memPoolSize);
Sets up the system memory heap so that malloc()/free() can be used after this call. It
is a good idea to check the arguments to this function if the system seems to be
having memory allocation problems.
sysClkConnect ((FUNCPTR) usrClock, 0);
10
Understanding the bootrom image AppNote- 237
sysClkRateSet (60 );
sysClkEnable ();
The above three lines connect the system tick interrupt and turn it on. From this
point on we will have system ticks occurring.(ie. taskDelay(), wdStart(),...etc can be
used).
selectInit (50 );
iosInit (20 , 50 , "/null");
consoleFd = (-1) ;
if (1 > 0)
{
ttyDrv();
for (ix = 0; ix < 1 ; ix++)
{
sprintf (tyName, "%s%d", "/tyCo/", ix);
(void) ttyDevCreate (tyName, sysSerialChanGet(ix), 512, 512);
if (ix == 0 )
{
strcpy (consoleName, tyName);
consoleFd = open (consoleName, 2 , 0);
(void) ioctl (consoleFd, 4 , 9600 );
(void) ioctl (consoleFd, 3 ,
0x01 | 0x02 | 0x04 | 0x08 );
}
}
}
ioGlobalStdSet (0 , consoleFd);
ioGlobalStdSet (1 , consoleFd);
ioGlobalStdSet (2 , consoleFd);
The above lines initialize the I/O system and set the system console to be the
"/tyCo/0" device and direct STDIN, STDOUT, and STDERR to this device. At this point
the system should be able to write to the serial device if the BSP is configured
correctly for the Hardware.
pipeDrv ();
excShowInit ();
excInit ();
excHookAdd ((FUNCPTR) bootExcHandler);
The above lines install the pipe driver, setup the exception printout routines, star t
the tExcTask, and install the boot exception handling routine. Generally the system
will not have any problems with any of these.
11
Understanding the bootrom image AppNote- 237
logInit (consoleFd, 5);
bootElfInit ();
The logInit() call will start the tLogTask and bootElfInit() will initialize the object
module reader to be ELF. It is doubtful that problems are here.
muxMaxBinds = 8 ;
if (muxLibInit() == (-1) )
return;
for (count = 0, pDevTbl = endDevTbl; pDevTbl->endLoadFunc != ((void *)0) ;
pDevTbl++, count++)
{
cookieTbl[count].pCookie = muxDevLoad (pDevTbl->unit, pDevTbl->endLoadFunc,
pDevTbl->endLoadString,
pDevTbl->endLoan, pDevTbl->pBSP);
if (cookieTbl[count].pCookie == ((void *)0) )
{
printf ("muxLoad failed!\n");
}
cookieTbl[count].unitNo=pDevTbl->unit;
bzero((void *)cookieTbl[count].devName,8 );
pDevTbl->endLoadFunc((char*)cookieTbl[count].devName, ((void *)0) );
}
The above lines will initialize the MUX layer and go through the endDevTbl[] array
and load each of the END devices specified in the table to the MUX. Then run each of
the END devices load functions. If the system has any problems here it may be due
to the END device configuration or the entries in the endDevTbl[] structure. The
endDevTbl[] array is typically found in the BSP file configNet.h.
taskSpawn ("tBoot", bootCmdTaskPriority, bootCmdTaskOptions,
bootCmdTaskStackSize, (FUNCPTR) bootCmdLoop,
0,0,0,0,0,0,0,0,0,0);
This spawns the bootCmdLoop() function which creates the interface the user sees
on the serial device for specifying the boot device and image to grab for execution.
Below is the post processed bootCmdLoop() function that implements the interface.
We will not go through it line by line but the developer should understand it as it is a
good place to put in some custom functionality (ie. debugging help, reflashing
routines,...etc.).
static void bootCmdLoop (void)
{
BOOT_PARAMS params;
char line [255 ];
12
Understanding the bootrom image AppNote- 237
char *pLine;
int nwords;
int nbytes;
int value;
int adr;
int adr2;
FUNCPTR entry;
char key = 0;
(void) ioctl (0 , 2 , 0 );
if (sysStartType & 0x02 )
printBootLogo ();
usrBootLineInit (sysStartType);
printExcMsg (sysExcMsg);
*sysExcMsg = '\0' ;
bootStringToStruct (((char *) (0x00000000 + 0x4200 )) , ¶ms);
sysFlags = params.flags;
if (!(sysStartType & 0x01 ) &&
!(sysFlags & 0x04 ))
{
int timeout = 7 ;
if ((sysStartType & 0x04 ) ||
(sysFlags & 0x08 ))
{
timeout = 1;
}
key = autoboot (timeout);
}
(void) ioctl (consoleFd, 3 , (0x01 | 0x02 | 0x04 | 0x10 | 0x08 | 0x20 | 0x40
) );
printf ("\n");
for (;;)
{
if (key == '@')
{
line [0] = key;
line [1] = '\0' ;
key = 0;
13
Understanding the bootrom image AppNote- 237
}
else
{
printf ("[VxWorks Boot]: ");
fioRdString (0 , line, sizeof (line));
}
adr = adr2 = 0;
nwords = 0;
pLine = line + strlen (line) - 1;
while ((pLine >= line) && (*pLine == ' '))
{
*pLine = '\0' ;
pLine--;
}
pLine = line;
skipSpace (&pLine);
switch (*(pLine++))
{
case '\0' :
break;
case 'd':
if ((getArg (&pLine, &adr, 1 , 1 ) == 0 ) &&
(getArg (&pLine, &nwords, 0 , 1 ) == 0 ))
d ((char *) adr, nwords);
break;
case 'e':
printExcMsg (sysExcMsg + 1);
break;
case 'f':
if ((getArg (&pLine, &adr, 1 , ! 1 ) == 0 ) &&
(getArg (&pLine, &nbytes, 0 , ! 1 ) == 0 ) &&
(getArg (&pLine, &value, 0 , ! 1 ) == 0 ))
{
bfillBytes ((char *) adr, nbytes, value);
}
break;
case 't':
if ((getArg (&pLine, &adr, 1 , ! 1 ) == 0 ) &&
(getArg (&pLine, &adr2, 1 , ! 1 ) == 0 ) &&
(getArg (&pLine, &nbytes, 1 , ! 1 ) == 0 ))
{
bcopy ((char *) adr, (char *) adr2, nbytes);
14
Understanding the bootrom image AppNote- 237
}
break;
case 'm':
if (getArg (&pLine, &adr, 1 , ! 1 ) == 0 )
m ((char *) adr);
break;
case 'p':
bootParamsShow (((char *) (0x00000000 + 0x4200 )) );
break;
case 'c':
bootParamsPrompt (((char *) (0x00000000 + 0x4200 )) );
if (strlen(((char *) (0x00000000 + 0x4200 )) ) <= 255 )
{
if ((sysNvRamSet (((char *) (0x00000000 + 0x4200 )) ,
strlen (((char *) (0x00000000 + 0x4200 )) ) + 1, 0)) == (-1) )
printf("\nNOTE: Bootline not saved to NVRAM\n");
}
else
printf("\nWARNING:Bootline longer than BOOT_LINE_SIZE (%d bytes).
Not saved to NVRAM\n", (int) 255 );
break;
case 'g':
if (getArg (&pLine, (int *) &entry, 1 , ! 1 ) == 0 )
go (entry);
break;
case 'n':
netifAdrsPrint (pLine);
break;
case '?':
case 'h':
bootHelp ();
break;
case '@':
case '$':
if (bootLoad (pLine, &entry) == 0 )
{
go (entry);
}
else
{
taskDelay (sysClkRateGet ());
reboot (0x01 );
}
15
Understanding the bootrom image AppNote- 237
break;
case 'l':
if (bootLoad (pLine, &entry) == 0 )
{
printf ("entry = 0x%x\n", (int) entry);
}
else
{
taskDelay (sysClkRateGet ());
reboot (0x01 );
}
break;
case 'v':
printBootLogo ();
break;
default:
printf ("Unrecognized command. Type '?' for help.\n");
break;
}
}
}
That concludes the code walk through of what it takes to get to the vxWorks boot
screen.
Sometimes it is useful to build in other source code into the bootrom image. To build
source code into the ROM executable (ie. RAM_LOW_ADRS executable) the
developer would set the BOOT_EXTRA macro in the BSP’s Makefile to this,
BOOT_EXTRA = myOwnCode.o. That would cause the file myOwnCode.c to be
compiled into myOwnCode.o then linked into the final object.
In the case of the bootrom_uncmp image or the RAM_HIGH_ADRS executable part
of the bootrom image the developer would need to modify the macro MACH_EXTRA
in the BSP’s Makefile. The line MACH_EXTRA = myOwnCode.o would compile and
link the myOwnCode.c file into that image.
We will now focus on debugging techniques for when things go wrong during
runtime.
16
Understanding the bootrom image AppNote- 237
Debugging Techniques
If the developer has a visionICE or visionPROBE and the proper connections on the
hardware then debugging should be much easier. The following items should be
checked when debugging the bootrom and bootrom_uncmp images:
?? Operating system components built into image.
?? Location of ROM and RAM images.
?? Runtime execution.
Operating system components built into image
First, lets make sure the executable image contains the operating system
components desired. Remember the developer will need to have set up the
environment variables in order to run these tools. The easiest way to do this is to use
the torVars script mentioned near the beginning of this document.
The developer can see if components are included at compile time by using the
#warning macro. This is a preprocessor trick that will print out a message during
compilation.
Just add a line similar to #warning We are within the expected conditional code
segment to the code. If that message shows up during compilation the code is
included. Here is a simple example:
#ifdef LOCAL_MEM_AUTOSIZE
/* To Do Autosizeing stuff */
/* This BSP is not supporting Autosizing */
#warning We should NOT be in here.
#else /* not LOCAL_MEM_AUTOSIZE */
/* Don't do auto-sizing, use defined constants. */
#warning We should be here.
sysPhysMemSize = (char *)(LOCAL_MEM_LOCAL_ADRS + LOCAL_MEM_SIZE);
#endif /* LOCAL_MEM_AUTOSIZE */
Upon building the developer may see this:
ccppc -mcpu=603 -mstrict-align -ansi -O2 -fvolatile -fno-builtin -Wall -I/h -I. -
IC:\T22\ppc\target\config\all -IC:\T22\ppc\target/h -IC:\T22\ppc\target/src/config -
IC:\T22\ppc\target/src/drv -DCPU=PPC603 -DTOOL_FAMILY=gnu -DTOOL=gnu-c sysLib.c
sysLib.c:528: warning: #warning We should be here.
This can be helpful when the developer is not sure a component is being included or
not. The other option to this is to generate the post processed output and just look at
17
Understanding the bootrom image AppNote- 237
that. See previous description for how to do this. This is helpful to confirm that the
expected include file is getting picked up before some other include file.
Another way to see if components are included is to look at the nm<arch> -n output.
Here is an example:
C:\T22\ppc\target\config\wrSbc824x>nmppc -n bootrom
00100000 T _romInit
00100000 T _wrs_kernel_text_start
00100000 T romInit
00100000 T wrs_kernel_text_start
00100038 t cold
00100044 t warm
00100048 t start
001000a4 t ifpdr_value
00100230 t romInit824x
00100680 t romInvalidateTLBs
00100694 t tlbloop
001006a8 t romMinimumBATsInit
0010073c t gcc2_compiled.
0010073c T romStart
00100888 t copyLongs
0010090c t fillLongs
00100964 t gcc2_compiled.
00100964 t gcc2_compiled.
00100964 t memcpy
001009bc t bzero
00100a04 t adler32
00100bb8 t cksum
00100c6c t zcalloc
00100cbc t zcfree
00100d38 t huft_build
001013ec t inflate_trees_bits
00101480 t inflate_trees_dynamic
001015f8 t falloc
00101618 t inflate_trees_fixed
00101814 t inflate_trees_free
00101898 t inflate_flush
00101a70 t inflate_fast
00101e78 t inflate_codes_new
00101f00 t inflate_codes
0010272c t inflate_codes_free
00102764 t inflate_blocks_reset
0010284c t inflate_blocks_new
18
Understanding the bootrom image AppNote- 237
0010292c t inflate_blocks
001035f8 t inflate_blocks_free
00103678 t inflateReset
001036f4 t inflateEnd
00103780 t inflateInit
00103880 t zinflate
00103db4 T inflate
00104190 A _etext
00104190 D _wrs_kernel_data_start
00104190 A _wrs_kernel_text_end
00104190 A etext
00104190 D runtimeName
00104190 D wrs_kernel_data_start
00104190 A wrs_kernel_text_end
00104194 D runtimeVersion
00104198 D vxWorksVersion
0010419c D creationDate
001041a0 D _binArrayStart
001041a0 D binArrayStart
0010c190 T _SDA2_BASE_
001363d0 D _binArrayEnd
001363d0 D binArrayEnd
001363d0 d cplens
0013644c d cplext
001364c8 d cpdist
00136540 d cpdext
001365b8 d inflate_mask
001365fc d border
00136648 d fixed_built
0013664c d fixed_bl
00136650 d fixed_bd
00136654 d fixed_tl
00136658 d fixed_td
0013665c d buf
00136660 d nextBlock
00136664 D inflateCksum
00136670 A _edata
00136670 B _wrs_kernel_bss_start
00136670 A _wrs_kernel_data_end
00136670 A edata
00136670 b fixed_mem
00136670 B wrs_kernel_bss_start
00136670 A wrs_kernel_data_end
19
Understanding the bootrom image AppNote- 237
00137700 b intBuf
0013e660 D _gp
0013e668 D _SDA_BASE_
0014fda8 b c
0014fde8 b u
0014fe24 b v
001502a4 b x
001502f0 A _end
001502f0 A _wrs_kernel_bss_end
001502f0 A end
001502f0 A wrs_kernel_bss_end
Since this is the bootrom image it is not exactly interesting, but the developer could
just as well look at the tmp.o image to see which components are getting included
within the vxWorks kernel for the bootcode. The bootrom_uncmp image will contain
the full information if that is the target build, since there is no tmp.o created.
Location of ROM and RAM images
Next, lets make sure the image is in the right places in ROM and RAM:
To do this we will use two utilities: nm<arch> -n and objdump<arch> -D. For
example in the above nmppc -n output we see some pretty key values.
00100000 T romInit - Start of execution address in RAM, should be
RAM_LOW_ADRS.
00104190 A etext - End of text.
001041a0 D binArrayStart - start of data array holding compressed bootcode
image.
001363d0 D binArrayEnd - end of data array holding compressed bootcode image.
00136670 A edata - end of data.
001502f0 A end - end of BSS.
The developer may be thinking these addresses should be mapped to ROM and the
reset vector. Keep in mind that this image starts out running in ROM and is copied to
RAM_LOW_ADRS by romStart() and then continues execution from RAM, that is why
it is linked to RAM. All the function calls within ROM are done position
20
Understanding the bootrom image AppNote- 237
independently. But remember, this code is placed in ROM such that the romInit()
entry point is at the power on reset vector.
Note that the link value of "end" MUST BE less than RAM_HIGH_ADRS.
Now lets look at the key nm<arch> -n output values of tmp.o, but first we must
modify a Makefile include file to keep tmp.o around after the build. Edit the file
$(WIND_BASE)/target/h/make/rules.bsp and go to the bootrom.Z.s target. Comment
out the second occurrence of - @ $(RM) tmp.o.
This will cause tmp.o to NOT be deleted upon building bootrom. Then examine tmp.o
for components included in the bootcode executable. Below are some key values
taken from the nmppc –n tmp.o output.
01f00000 T compressedEntry - stub entry point that calls usrInit(). In the
bootConfig.c file.
01f000bc T usrInit - code to bring up the vxWorks kernel.
01f7d200 A etext - end of Text.
01f7eac0 A edata - end of Data.
01f82160 A end - end of BSS.
Note that the value of "end" MUST BE small enough to allow heap space between
"end" and sysMemTop(). The compressedEntry() function is linked at
RAM_HIGH_ADRS and is just a stub function that calls usrInit().
That pretty much covers the image layout in ROM and RAM. lets look at a few key
macro's the developer may have to deal with to build the image. These are discussed
in several other documents so we will not go into great detail about them here.
LOCAL_MEM_LOCAL_ADRS - start of RAM where vxWorks application will be
running.
LOCAL_MEM_SIZE - size of RAM where vxWorks application will be running.
RAM_LOW_ADRS - location in RAM where vxWorks application image is linked to. It
is also the location in RAM where the bootrom links romInit() and the
decompression code.
RAM_HIGH_ADRS - location in RAM where the bootcode executable is linked. Note
that the difference between RAM_HIGH_ADRS and RAM_LOW_ADRS MUST be large
enough to contain the vxWorks application image as it is loaded.
ROM_TEXT_ADRS - Address of reset vector in ROM.
21
Understanding the bootrom image AppNote- 237
USER_RESERVED - indicates the size of memory reserved at the end of the
LOCAL_MEM_LOCAL_ADRS space for usage outside the vxWorks kernel.
Now lets look at HEX files for a bit. Below is the extra build output when creating
bootrom.hex. Notice the utility to do the conversion from ELF to hex is
objcopy<arch>.
C:\T22\ppc\host\x86-win32\bin\objcopyppc -O srec --gap-fill=0 bootrom out.tmp1
C:\T22\ppc\host\x86-win32\bin\objcopyppc -O srec --ignore-vma --set-start=0x0 out.tmp1
bootrom.hex
This HEX file is really a Motorola S-Record file that is assumed to start at address 0.
The developer will need to perform whatever bias/offset needed with the flash
programmer to get this to start at the reset vector. The developer should consult the
flash programmer documentation for specific details to do this. When using vision
tools to do the programming the developer will be converting this HEX file to a BIN
file. The BIN file that the vision tools generate is a special format specific to the
vision tools and not the more common BIN format. So beware when planning to use
the BIN file between vision tools and other flash programmers. Below is some data
from the bootrom.hex file.
S00E0000626F6F74726F6D2E6865787C
S21400000048000039600000004800003D436F7079EA
S214000010726967687420313938342D3230303120B7
S21400002057696E64205269766572205379737465D9
...
...
S2140366400000000E000000010000000F0000000024
S2140366500000000000000000000000000000000032
S21403666000137708001377080000000000000000FE
S804000000FB
To check the validity of this file we can do a simple calculation. Just subtract the last
address from the first address and get the size of the data to be programmed:
To get the first address, look at the second line in the HEX file.
S 2 14 000000 48 00 00 39 60 00 00 00 48 00 00 3D 43 6F 70 79 EA
S - indicates we are an S- record.
1 - indicates we have 16-bit addressing
22
Understanding the bootrom image AppNote- 237
2 - indicates we have 24-bit addressing
3 - indicates we have 32-bit addressing
The next two bytes are record length in hexadecimal.
14 - gives us 16 data bytes + 1 checksum byte + 3 address bytes for a total of 0x14
bytes.
000000 - indicates the address.
The second to last line in the HEX file is
S 214 036660 00137708001377080000000000000000FE
036660 - indicates the starting address but we filled data up through the 0x3666f
address. So the end is really 0x36670.
In this example we have 0x036670 - 0x000000 = 0x036670. Notice that edata –
romInit (eg. 0x36670 - 0x000000) is 0x033670 from the above nm<arch> -n output.
This gives our HEX file some validity. It is always a good idea to double check this if
things are not working as expected.
Another good check is to use the objdump<arch> -D command to look at the
disassembly of the executable. Here is a small example of objdumpppc -D bootrom
below.
bootrom: file format elf32-powerpc
Disassembly of section .text:
00100000 <_romInit>:
100000: 48 00 00 39 bl 100038 <cold>
nop
100004: 60 00 00 00 bl 100044 <warm>
.long 0x436f7079
100008: 48 00 00 3d andi. r9,r19,26472
andis. r0,r1,12601
10000c: 43 6f 70 79 addi r1,r20,11570
addic r1,r16,12576
100010: 72 69 67 68 rlwinm r9,r27,13,25,18
subfic r2,r18,26998
100014: 74 20 31 39
100018: 38 34 2d 32
10001c: 30 30 31 20
100020: 57 69 6e 64
100024: 20 52 69 76
<rest of output cut off here>
Notice how the data in the above S-record matches up with the data in the
disassembly.
48 00 00 39
23
Understanding the bootrom image AppNote- 237
60 00 00 00
48 00 00 3d
43 6f 70 79
vs
48 00 00 39 60 00 00 00 48 00 00 3D 43 6F 70 79
After programming the Flash, read back the Flash contents to confirm that the
Flash has been programmed correctly. Something the developer should be checking
when bringing up the board for the first time to confirm the development processes
are valid.
Looking at the disassembly of an object file is helpful when given the address of an
exception and the developer needs to find out where it was in the code. The
objdum<arch> -D command works great for that.
The above tools should help the developer confirm the location of the images in ROM
and RAM. Once that is done, we can now focus on runtime execution.
Runtime execution
At this point we are building our image and programming the Flash with it. The
image is located in the correct area and we are ready to power on and run the
bootrom code. We now need a way to debug the runtime image. Lets make the
assumption that the developer does not have an ICE debug tool, but the developer
does have at least one LED that can be used or a port pin that can be connected to a
logic analyzer.
One useful bit of code the developer can add to the bootConfig.c file or the bootInit.c
file would be some LED routines like the ones below. The developer would add this
code to bootConfig.c if the system has already started to execute the
RAM_HIGH_ADRS code in RAM. The developer would add this code to the bootInit.c
file when needing to debug the romInit() and romStart() function. Basically, the
developer needs to make sure that the image gets uncompressed to the
RAM_HIGH_ADRS area correctly. If the system does not have an LED to use then set
these routines up to use a port pin that the developer can read with the logic
analyzer. Expect to be very creative to see what is happening within the system.
24
Understanding the bootrom image AppNote- 237
Note that the addresses and values used in the below example are hardware specific
and will almost certainly be different for the new hardware.
void ledEnable(void)
{
*(short *)(0x306102) = 0x8000; /* code to turn enable the LED to be used */
}
void ledOn(void)
{
*(short *)(0x306100) = 0x8000; /* code to turn on the LED */
}
void ledOff(void)
{
*(short *)(0x306100) = 0x0; /* code to turn off the LED */
}
void ledBlink(int n)
{
int i,j;
/*
* The 200000 value is picked to be around 1 second of delay. Adjust as needed.
*/
ledEnable();
for(j=0; j < n; j++) {
for(i=0; i < 200000; i++) ledOn();
for(i=0; i < 200000; i++) ledOff();
}
/* just to create a pause between blinks */
for(i=0; i < 200000; i++) ledOff();
}
The call to make is the ledBlink() call. It is useful to start off with ledBlink(1) and
then use ledBlink(2) ...etc. It is an easy way to tell where the system is within the
code as it is executing. This is a powerful runtime tool that should get the developer
to the point of seeing the vxWorks boot banner and menu system on the serial
output device. Once that happens the developer should be able to use printf()’s to
complete the task or some of the debug facilities already built into the boot menu.
25
Understanding the bootrom image AppNote- 237
Even before printf()’s are available some debug information can be output on the
serial device after sysHwInit() has been called. Add the following code to the BSP’s
sysLib.c file. This should work with any BSP whose serial driver can be operated in
polled mode output.
void sysPrintDebug(char *msg)
{
unsigned long msgIx;
for (msgIx = 0; msgIx < strlen(msg); msgIx++)
while (sioPollOutput (sysSerialChanGet(0), msg[msgIx]) == EAGAIN);
}
You'll need to format your end lines with “\r\n” to get both carriage return and line
feed. For example:
sysPrintDebug("Made it to sysHwInit2().\r\n");
Now lets assume the developer does have an ICE debug tool and needs to debug the
bootrom code that is running from RAM. One problem, is stopping program
execution when there is no chance to set up software breakpoints in the RAM
executable. Remember that the RAM executable is uncompressed into the
RAM_HIGH_ADRS location and starts running when romStart() jumps to it. A way to
stop the code in its tracks is to put an infinite loop in the romStart() code after the
inflate() call or at the beginning of the usrInit() code. That way when the infinite loop
is hit, the developer has the ability to set software breakpoints in the now existing
code at the RAM_HIGH_ADRS location. After setting up the desired breakpoints the
developer can then modify the program counter with the ICE tool to proceed with the
next instruction. The breakpoint should be hit and the developer can now step
through the code for the RAM executable part of the bootrom. There is a macro
called FOREVER within the vxWorks.h include file that implements this infinite loop,
in C code as for(;;), if the developer cares to use it.
Summary
The bootrom and bootrom_uncmp images can be easily understood when the time is
taken to understand the build model and code execution path. Knowing which code
26
Understanding the bootrom image AppNote- 237
executes from ROM and which code executes from RAM helps in determining the
best debug techniques to be used.
This Application Note helps with:
?? Getting the post-processed output from the C compiler.
?? Examining the object files using the tools nm<arch> and objdump<arch>.
?? Confirming that executables are located in the correct place in ROM and RAM.
?? Building in custom code to the bootrom and bootrom_uncmp images.
?? Using the runtime debug tips and knowing when they can be used.
These tools should help any developer obtain a successful bootrom image. Good
Luck.
References
VxWorks Programmer’s Guide.
BSP Developer’s Kit.
GNU ToolKit User’s Guide.
VxWorks Reference Manual.
GNU Make User’s Guide
VxWorks for PPC architecture Supplement
27