Tutorial on Configuring, Building, and Debugging
FBTC programs
This tutorial gives information on configuring FlashBurn for a custom
hardware target. This process involves modifying, building and running a
FlashBurn Target Component (FBTC) application that will execute in RAM on the
custom hardware target.
For more information on the architecture of FlashBurn, see
How FlashBurn Works
.
This tutorial is intended to supplement, not replace, the information in the
FlashBurn Programmer's Reference Guide delivered with the porting kit.
For this example, the custom hardware used was a DM642EVM board
from Spectrum Digital. The on-board flash is an AMD AM29LV033C and
the FBTC program used as a starting point was for the DSK6416.
Gathering the necessary tools
The FBTC program being modified will execute in RAM on the target
hardware. Modifying this program will require the same tools and
knowledge needed to build an embedded application on the target hardware.
Specifically, the following will be needed:
-
Code Composer Studio 2.20 or 3.x
-
A JTAG emulator
-
Knowledge of the target hardware
Our example will use Code Composer 3.1 and the Spectrum Digital
XDS510 USB emulator.
Gathering the necessary information
Before starting, gather the following information about the target hardware:
-
The memory layout, including the flash memory location and size
-
Any chip-enable settings necessary to allow the DSP to write to and read from
the flash memory
-
A data sheet on the flash memory, including the protocol for enabling, reading,
writing and erasing the flash
The necessary information for the DM642 EVM is:
-
Flash size is 0x40 0000 (4 Mbytes)
-
Flash is mapped 512 Mbytes at a time into CE1 EMIF address
space 0x9000 0000 .. 0x9007 FFFF.
-
To address all of flash, the DM642 provides bits A0..A18 of
flash address, the FPGA provides A19..A21 of flash address
Reading a flash data sheet
The flash memory data sheet will likely have a separate section describing the
commands for reading, writing, and erasing the flash memory. Often,
these commands are summarized in a table similar to the one shown below, for
the AM29LV033 flash.

Using this table we have all the information we need to port our
FBTC program to the EVMDM642 board:
-
Chip Erase is achieved by writing a sequence of
0xAA, 0x55, 0x80, 0xAA, 0x55, 0x10 to any location in the flash (we
choose location 0)
-
Reset to Read mode sequence is: 0xF0
to any location
-
Reading the flash is simply reading as if the flash were RAM
(once the Reset has been performed)
-
Writing the flash is the sequence
0xAA, 0x55, 0xA0 to any location, then writing the flash location
with 1 byte of data.
Choosing from the example FBTC programs
The FlashBurn Porting Kit ships with example FBTC programs for many of the TI
DSK boards. These examples include full source code and CCS
projects, These may be found in the examples/
directory of the FlashBurn installation. Choosing one of these FBTC
programs as a starting point is a good idea.
It is best to choose an FBTC from the same ISA family as the custom hardware
board. For example, for a C55x-based custom hardware, the DSK5510
FBTC program is a good choice to use as a starting point. Copy the
entire sample directory to your work area and begin work.
There may be reason to consult the other FBTC programs as you go.
For example, the flash memory on the custom hardware may be closer in behavior
to the flash memory in one of the other TI DSKs. Consulting the
BurnFlash() functions in that FBTC program may provide good insight and
reusable code.
The DSK6416 example was chosen as a starting point.
The directory DSK6416/ was copied
from the examples directory and all files named *DSK6416* were renamed to
*EVMDM642*. In addition, the Code Composer project file (.prj) was
edited with a text editor and "DSK6416" was replaced with "EVMDM642".
Ensuring that Code Composer is properly configured for the hardware
Many of the first-time problems that users encounter with FlashBurn are due to
Code Composer not being properly configured for the target
hardware. Check the FlashBurn FAQ for
tips on Code Composer settings and common errors that can occur.
Using Code Composer, the hello1.out
example program was loaded from the Code Composer tutorials/
directory and executed, to make certain that Code Composer, the emulator
and the DM642 EVM were properly communicating.
Initial build of the FBTC program before making any source code changes
Before making any changes to the FBTC program to support flash programming, it
is useful to get the program built and to test that it can be downloaded and
started on the hardware. This will resolve any linker configuration
or hardware configuration issues prior to ever modifying the FBTC program.
As described above, the DSK6416/
directory was copied and the directory and files were renamed with EVMDM462.
The Code Composer project build options were set to Full Debug
and No Optimization.
The linker command file from the FBTCDSK6416 was able to be used
as-is for the DM642 EVM. The project built and produced an output
file FBTCEVMDM642.out.
Using Code Composer Studio, the file
FBTCEVMDM642.out was loaded - it loaded successfully. A
breakpoint was placed in the function doMessageProc(),
and the program was executed. Execution halted at
doMessageProc(). The program appears to be running OK.
First execution of the FBTC with FlashBurn
Having demonstrated that the FBTC program will build, load, and run on the
DM642, it is time to ensure that the FlashBurn host application can communicate
with the FBTC program.
This is best accomplished by leaving Code Composer Studio open (with the
FlashBurn project loaded), and using FlashBurn to download the FBTC program.
Loading FlashBurn and choosing the DM642 EVM target:

Choosing the FBTC program that was just built:

Downloading the FBTC program, which starts it running in Code
Composer, until it hits the breakpoing at doMessageProc().

Notice that the FBTC program has reported back to FlashBurn the
flash base address and flash size that is configured in the FBTC. These
remain from the DSK6416 configuration and are incorrect for the
DM642. We will change them later.
If this step succeeded for you, Great! You are ready to modify the
FBTC for your particular flash memory. If not, something has gone
awry and you will be debugging the FBTC as described in the next
section. The indications that things did not go correctly
are:
-
The buttons in Step 3 (Erase, Program, View) Flash are not enabled.
-
The Flash Base Address and Flash Size boxes are not filled in
-
Your Code Composer Studio is reporting the target as "Running", or reporting an
error communicating with the hardware
Debugging the FBTC program
The FBTC program is a DSP program like any other. It is downloaded
to RAM and executed. Debugging the FBTC program using Code Composer
Studio is the best way to determine the source of any problems.
To debug the FBTC:
-
Make sure the FBTC program is built with Full Symbolic Debug and No
Optimizations
-
Start Code Composer first, and load the FBTC project file
-
Run the FlashBurn application, and perform Steps 1 and 2 to observe the FBTC
being downloaded and started
Some notes on the behavior of the FBTC:
-
Like most embedded system programs, the FBTC runs in an endless
loop. The main() function calls
doMessageProc() and doCommand().
The function doMessageProc() is simply a
placeholder for a breakpoint used to synchronize communication between the
FlashBurn host app and the FBTC.
-
For each action that FlashBurn requests and the FBTC performs, the following
actions occur:
-
(the FBTC is halted at doMessageProc())
-
FlashBurn writes a command message into the buffer theMessage[]
-
FlashBurn starts the FBTC running
-
The FBTC enters the doCommand()
function
-
The doCommand() function dispatches to other
functions (BurnFlash(), GetBaseAddress(),
...) to perform the action
-
A status is written back to theMessage[]
-
The FBTC leaves doCommand() and returns to the
loop in main()
-
The FBTC halts in doMessageProc()
-
Debugging the FBTC may cause FlashBurn to time-out waiting for a response from
the target. Closing and restarting FlashBurn may be necessary.
When the FBTC is first loaded in Step 2 of the FlashBurn host program (Download
FBTC), the following FBTC commands are executed. If these commands
do not succeed, the buttons in Step 3 remain disabled.
-
FBTC_GET_PROTOCOL_VERSION
-
FBTC_GET_MAX_BLOCKSIZE
-
FBTC_GET_FBTC_VERSION
-
FBTC_GET_BASE_ADDR
-
FBTC_GET_FLASHSIZE
Refer to the FlashBurn Programmer's reference for more information on FBTC
commands.
Enabling debug output from the FBTC
The FBTC program has the ability to echo debug information to the Code Composer
Output window via printf(). This
capability is enabled by setting the macro DBPRINT
to 1 at the top of the FBTCMain.c
and FBTCTarget.c
files.
Enabling DBPRINT will cause the FBTC program to
be larger. Changes may be needed to the FBTC linker control file in
order to link.
The DM642 linker control file was modified to move
.sysmem, .cio, and
.cinit from PMEM to
BMEM.
Performing Step 2 in FlashBurn (Download FBTC) now produces the
following output in Code Composer Studio:
FBTC Configuration Step #1 - Flash memory and board configuration
The first configuration setting to modify is the flash base address and size in
TargetConfig.h. Modify FLBASE
and FLSIZEBYTES to reflect the custom hardware.
Unless you have chosen a FBTC from another ISA family to use as a starting
point, do not modify BYTESPERMAU,
FLASH_DATA_TYPE, MSG_DATA_TYPE or
PTR_SIZED_INT.
For the DM642, the following changes were made in
TargetConfig.h
/**
* DM642 flash is accessed thru 0x90000000 .. 0x9007FFFF with the FPGA used as a page register
* for the high 3 bits of the 4Mbyte address space.
*/
#define FLBASE 0x90000000
#define FLSIZEBYTES 0x00400000
The following changes were made in FBTCEVMDM642.c:
static u32 * volatile EMIF_CE1 = (u32 *)0x1a80004;
#define CE1_8 0xF3A88E02 /* reg to set CE1 as 8bit async */
#define CE1_32 0xF3A88E22 /* reg to set CE1 as 32bit async */
Now, FlashBurn reports the correct base address and size for the
flash:

Configuration Step #2 - Flash Addressing
The next configuration step is dealing with flash addressing. As
described in the FlashBurn Programmer's Reference Manual, FlashBurn treats the
flash image as a stream of bytes, starting with an index of zero.
The FBTC program maps a flash index to an actual flash address on
the target hardware. This mapping is achieved by the
GetFlashAddr() function.
For the EVMDM642, GetFlashAddr() needs to only return bits 0..18
of the address, and a new function SetFlashPage() is written to set the FPGA
page register.
/**
* Convert an index to a flash address. On the EVMDM642, the 4Mbyte flash
* is mapped into the first 0x80000 bytes of CE1 space. The FPGA is programmed
* with the upper 3 bits of the address in SetFlashPage()
*
* @param [in] index - The flash index
*
* @return The flash address in CE1 corresponding to the given flash index
*/
FLASH_DATA_TYPE* GetFlashAddr(FLASH_IMAGE_INDEX_TYPE index)
{
PTR_SIZED_INT addr = (u32)GetFlashBase() + index;
addr = addr & 0x9007FFFF;
return (FLASH_DATA_TYPE*)(addr);
}
/**
* Set the FPGA Flash Page Register to hold the upper 3 bits of the flash
* address corresponding to the given flash index.
*
* @param [in] index - The flash index
*
* @return void
* @post Flash Page register is set
*/
void SetFlashPage(FLASH_IMAGE_INDEX_TYPE index)
{
PTR_SIZED_INT addr = (u32)GetFlashBase() + index;
*FLASH_PAGE = (addr & 0x380000) >> 19;
}
Configuration Step #3 - Set Flash to Read Mode - ResetFlash()
Some flash memories have 'modes' for reading and writing. The function
ResetFlash() is intended to reset the flash to read mode. Implement
this function next, so the flash can be read reliably. The function
ResetFlash() is called in the FBTC whenever flash memory is read.
Implemented ResetFlash() according to the flash data sheet,
which is the same implementation as the DSK6416 - no changes needed.
/**
* Reset the flash to Read mode
*/
void ResetFlash()
{
volatile FLASH_DATA_TYPE* volatile flashaddr;
flashaddr = GetFlashAddr(0); /* Don't care what address is used */
*flashaddr = 0xF0;
}
Configuration Step #4 - Reading Flash memory
Reading Flash memory and populating the View Flash window will validate that
flash addressing (GetFlashAddr()) and flash
access (GetFlashVal(), SendFlashBufToHost())
are working correctly.
The implementation of GetFlashVal()
was modified to set the flash page using SetFlashPage().
/**
* Get the contents of a flash location
*
* @param [in] index - Zero-based index into flash memory
*
* @return - The contents of flash memory at the location
*/
FLASH_DATA_TYPE GetFlashVal(FLASH_IMAGE_INDEX_TYPE index)
{
volatile FLASH_DATA_TYPE* volatile flashaddr;
/* Set External Flash for byte-addressability */
*EMIF_CE1 = CE1_8; /* EMIF CE1 control, 8bit async */
ResetFlash();
#if DBPRINT
printf("Retrieving 1 byte of flash at %x\n", flashaddr);
#endif
flashaddr = GetFlashAddr(index);
SetFlashPage(index);
return *(u8 *)flashaddr ;
}
The implementation of SendFlashBufToHost()was
modified to use a loop instead of memcpy since the flash data may cross a flash
page boundary. The implementation below could be optimized by checking
for page boundary crossing.
/**
* Sends a block of flash bytes to host
*
* @param [in] index - Zero based index into flash memory
* @param [in] nBytes - Count of bytes to send
*
* @pre - InitFlash() has been run
* @pre - nBytes is not larger than MAXDATABYTES
* @post - Flash contents have been sent to the host
*/
void SendFlashBufToHost(u16 cmd, FLASH_IMAGE_INDEX_TYPE index, u32 nBytes)
{
volatile FLASH_DATA_TYPE* volatile flashaddr;
FLASH_IMAGE_INDEX_TYPE i = index;
int mIdx = 0;
SetCmd(cmd);
SetArg(0, nBytes);
/* Set External Flash for byte-addressability */
*EMIF_CE1 = CE1_8; /* EMIF CE1 control, 8bit async */
#if DBPRINT
printf("Sending %x bytes from flash at %x\n", nBytes, flashaddr);
#endif
/* Copy bytes from flash to the message buffer */
/* Cannot use memcpy here because the copy may cross a flash page */
while(nBytes--)
{
flashaddr = GetFlashAddr(i);
SetFlashPage(i);
theMessage[DATAINDEX + mIdx++] = *flashaddr;
i++;
}
}
Flash memory can now be correctly read from the target, and the
Address column shows the correct flash addresses. If
this is not the case, the implementation of either
SendFlashBuffToHost() or GetFlashAddresses()
is incorrect.

Reading the same address in Code Composer verifies that the
flash read is working correctly.

Configuration Step #4 - Erasing Flash
Programming flash erase involves modifying the EraseFlash()
and CheckFlashErase() functions, implementing
the chip erase protocol as described in the flash data sheet.
Implementing flash erase on AM29LV033C:
/**
* Erase all of flash memory
*
* @pre - InitFlash() has been run
* @post - All of flash is erased
*/
void EraseFlash(void)
{
/* Convenient pointers for sending commands to flash chip */
volatile FLASH_DATA_TYPE* volatile pBase = GetFlashBase();
/* Set External Flash for byte-addressability */
*EMIF_CE1 = CE1_8; /* EMIF CE1 control, 8bit async */
/* Code to erase AMD29LV033C */
*pBase = 0xAA;
*pBase = 0x55;
*pBase = 0x80;
*pBase = 0xAA;
*pBase = 0x55;
*pBase = 0x10;
CheckFlashErase();
ResetFlash();
return;
}
/**
* Check the flash erase status - return when the erasure is completed
*
* @pre - InitFlash() has been run
* @post - When the function returns, flash erasure is complete
*/
void CheckFlashErase(void)
{
/* Spin here 'til erasing completes
*/
while(GetFlashVal(0) != 0xff)
;/* Do Nothing!*/
}
Also, the 4Mbyte flash on the EVMDM642 takes slightly over 30
seconds to erase, so the definition of ERASEESTIMATE
in TargetConfig.h is set to
33. This value is used by the FlashBurn host application to display
the progress bar during flash erase.
Testing flash erase by erasing and then viewing flash:


Configuration Step #5 - Writing flash memory without verification
Once flash can reliably be read and erased, the final step is implementing the
flash programming function BurnFlash().
For parallel flash connected to the EMIF, this involves writes to memory
locations in the Flash. For serial flash, this may require writes to an
SPI or I2C bus from the DSP.
The EVMDM642 flash programming is similar to the DSK6416, except
that the 'flash paging' via the FPGA must be taken into consideration.
/**
* Burn data into flash memory at the given index
*
* @param [in] index - Zero based index into flash memory to begin burning
* @param [in] data - Data to burn to flash, from the command message
* @param [in] nBytes - Number of bytes to burn
*
* @pre - InitFlash() has been run
* @post - Flash at the current flash memory pointer is burned
*/
void BurnFlash(FLASH_IMAGE_INDEX_TYPE index, MSG_DATA_TYPE *data, u32 nBytes)
{
u16 timeout;
u8 c;
volatile FLASH_DATA_TYPE* volatile flashaddr;
FLASH_IMAGE_INDEX_TYPE i = index;
/* Convenient pointers for sending commands to flash chip */
volatile FLASH_DATA_TYPE* volatile pBase = GetFlashBase();
/* Set External Flash for byte-addressability */
*EMIF_CE1 = CE1_8; /* EMIF CE1 control, 8bit async */
#if DBPRINT
printf("Burning %x bytes of flash at %x\n", nBytes, flashaddr);
#endif
while(nBytes--)
{
flashaddr = GetFlashAddr(i);
SetFlashPage(i);
/**
* Prep AMD Flash Memory
* for writing a byte.
*/
*pBase = 0xAA;
*pBase = 0x55;
*pBase = 0xA0;
/* Write a byte */
*flashaddr = *data;
/* Spin here until programming completes
*/
c = *data++;
timeout = 0;
do
{
timeout += 1;
}while(*flashaddr != c && timeout < (u16)0xffff) ;
i++;
}
ResetFlash();
}
The flash programming was tested with a small .hex
file. Note that "Verify Write" was not checked. This will be
handled in the next step.

And, the results are visible in the View Flash Window:

Configuration Step #6 - Flash Checksum Computation
The computation of a flash checksum requires very little modification. The
only action required in CKSAccumBuf() is
dealing with any addressing peculiarities in the flash memory.
For EVMDM642, the paging of the flash required some slight
changes to CKSAccumBuf() to ensure that our
SetFlashPage() function was called to set the FPGA's Flash Page
Register properly:
/**
* Compute a checksum of a buffer
*
* @param [in] index - Zero based index into flash to start computing the
* checksum
* @param [in] nBytes - Length of flash to use for the checksum, in bytes
*
* @return - The value of the checksum
*
* @pre - InitFlash() has been run
* @post - Accumulated checksum is updated
*/
u16 CKSAccumBuf(FLASH_IMAGE_INDEX_TYPE index, u32 nBytes)
{
volatile FLASH_DATA_TYPE* volatile flashaddr;
FLASH_IMAGE_INDEX_TYPE i = index;
/* Set External Flash for byte-addressability */
*EMIF_CE1 = CE1_8; /* EMIF CE1 control, 8bit async */
#if DBPRINT
printf("Computing checksum for %x bytes of flash at %x\n", nBytes, flashaddr);
#endif
while(nBytes-- > 0)
{
flashaddr = GetFlashAddr(i);
SetFlashPage(i);
cksum += *flashaddr;
if(cksum > (unsigned long)0x0000ffff)
{
cksum += 1;
cksum &= (unsigned long)0x0000ffff;
}
i++;
}
return (u16)cksum;
}
Now, the flash can be programmed with verification on, and the
FBTC porting process is complete!
Summary
The FBTC program is a small application with one simple task - to accept a
stream of data, including a command in the buffer theMessage, and perform the
action specified by the command.
The complexity of porting the FBTC is mostly driven by the complexity of the
memory system on the target hardware. External serial flash is
slightly more complicated, and 'paged' flash introduces some extra work, as
shown in this example.
The following issues hold true, however, for any FBTC port:
-
It is necessary to configure Code Composer, your emulator, and your board
properly before starting a port.
-
Take small steps, and verify the results of each step, as shown in this
tutorial.
-
All of the changes will be in the files TargetConfig.h and FBTCBurntarget.c.
There should be no need to edit FBTCMain.c except to enable DBPRINT.
-
The FBTC program is a DSP program, and should be debugged using Code Composer
Studio like any DSP program
Following these steps will help you build a successful, robust FBTC program.
Please send comments and suggestions for improvement of this tutorial to
support@softwaredesignsolutions.com
|