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