1 Introduction In order to program the flash device on your target with the DSP on the board by using the Flash Programmer tool in ADSP-VisualDSP++ IDE, you need to provide your own flash driver. The Flash Programmer is a shell that communicates with the plug-in flash driver with a set of pre-defined commands. Therefore, your custom flash driver must follow the requirements. Although the flash programmer is used mainly for parallel flash devices, it can also be expanded to work with serial flash devices, FPGA devices, OTP parts, CPLDs, and coprocessors. If you can write application code to run on an Analog Devices processor to write, read, and erase the device, you can probably use the flash programmer to do this as well. This article describes how it works and shows you an example of flash driver for programming Atmel AT49BV040B device.
2 How the Flash Programmer Works
Having an understanding of the inner working of the Flash Programmer will help you write and debug your own driver.
2.1 Loading the Driver
Here is what happens when you use the Flash Programmer tool from the Tool menu.
• The processor is halted and reset • The flash programmer driver is loaded • The Flash Programmer window checks the driver for required symbols (AFP_BreakReady, AFP_Command, AFP_Offset, AFP_Buffer, AFP_Error, AFP_Size, AFP_Count, AFP_Stride, and AFP_NumSectors). • All public breakpoints are removed except ones that are used for printf. • A breakpoint is placed at AFP_BreakReady, and a run is issued stopping the processor at the breakpoint. • The Flash Programmer window checks the driver for optional symbols (AFP_Sector, AFP_Verify, AFP_Title, AFP_Desctription, AFP_DeviceCompany, AFP_BuildDate, AFP_DrvVersion, AFP_ManCode, and AFP_DevCode). None of these symbols is required for the driver to work properly. • There must be at least one sector, which means that AFP_NumSectors must be greater than or equal to 1. • A command is sent to get the manufacturer code and device code or reads them automatically if they already exist. They exist already if your code calls GetCodes() before the main processing loop. • A Command is sent to get the sector start and end offsets or read them automatically if they already exist. They exist already if your code calls GetSectorStartEnd() before the main processing loop. • The sector map is then updated with the sector start and end offset. The flash device’s size is determined by the end of the last sector.
2.2 Executing the Command
When each command is executed, the flash programmer driver always starts at AFP_BreakReady and ends at the same label after the processing of the command is done. Before executing a command, all necessary values from the window are written to the corresponding symbols in the driver, and then a run is issued.
For example, the following occurs to do a fill.
• Write the appropriate values from the window to AFP_Offset, AFP_Count, AFP_Stride, and AFP_Buffer. • Set AFP_Command to “FILL” (enum value 4). • Run until hitting the breakpoint at AFP_BreakReady.
Each command is executed the same way, but may have different symbols associated with it. In the example driver below, each command symbol is passed as parameter to the main function.
2.3 Programming Flash
If any of the pre-program erase options is set, an erase occurs before the flash is programmed. When programming a file to flash, the flash programmer writes only the number of bytes that are less than or equal to BUFFER_SIZE. If, for example, BUFFER_SIZE is 0x600, while the file is 0x700, the WRITE command is issued twice to write flash with the entire buffer. In the first pass, the flash programmer writes 0x600 bytes. In the second pass, it writes 0x100 bytes. Sending the WRITE command many times increases the time required to program a file into the flash. So, if possible, maximize BUFFER_SIZE, which is determined by the heap size specified in the Linker Description File (ldf) in your flash driver project.
2.4 Flash Programming Error Messages
The following error messages may occur.
Flash is not large enough to fill bytes This error indicates that the offset you are writing to plus the count of the how much to write is larger than the last sector’s end offset. Sometimes this happens when there is a change in the extended linear address in the Intel hex file being used. If you receive this error and your code is smaller than the flash size, look at the GetSectorStartEnd() function to ensure that it is correct. If this function proves correct, there might be a problem in your Intel hex file where the extended linear address causes the data to jump past the end of flash.
Polling toggle bit failed There could be a problem in the PollToggleBit() function. In many cases, you might be writing to an area that was not previously erased. Be aware when doing a fill, you are responsible for erasing the sections being written.
The driver was not at AFP_BreakReady This breakpoint is the starting and ending point for each command. When the driver is loaded, the flash programmer driver should be halted at this breakpoint. If this error message occurs, check your main function.
Could not allocate storage for the buffer When doing a malloc for AFP_Buffer, if there is not enough memory allocated for the heap in the ldf file, this error is generated. For example, if the heap is defined to be a size of 0x1000 bytes and we try to malloc 0x2000 bytes, a failure occurs and this message appears.
There was a problem with one or more required expression in the driver This error indicates that one of the required symbols was not found in the driver. The symbol that is missing should be contained in the error message.
Processing custom command failed. Make sure custom command is valid and try again Ensure that your custom command is handled in the driver’s main switch statement.
3 Write a Flash Driver
The flash driver shall consist of two layers of code. The upper layer communicates with the Flash Programmer, i.e., receives commands and takes actions, and returns action results; the lower layer is a device driver that interfaces with the hardware.
3.1 The Upper Layer
The upper layer declares all the required and optional global variables and provides functions for each command.
//flash programmer required symbols AFP_CMD_TYPE AFP_Command = NO_COMMAND; // command sent down from the GUI long AFP_Offset = 0x0; // offset into flash long AFP_Error = 0; // contains last error encountered long AFP_Size = BUFFER_SIZE; // buffer size long AFP_Count = -1; // count of locations to be read or written long AFP_Stride = -1; // stride used when reading or writing long AFP_NumSectors = NUM_SECTORS; // number of sectors in the flash device char *AFP_Buffer; // buffer used to read and write flash
//flash programmer optional symbols char *AFP_Title; // plug-in driver title char *AFP_Description; // device description char *AFP_DeviceCompany; // device company char *AFP_DrvVersion = "1.01.0"; // driver version char *AFP_BuildDate = __DATE__; // driver build Date long *AFP_SectorInfo; // sector start and end addresses char AFP_ManCode = -1; // 0x1F = Atmel char AFP_DevCode = -1; // 0x13 = AT49BV040 long AFP_Sector = -1; // sector number bool AFP_Verify = true; // verify writes or not long AFP_StartOff = 0x0; // sector start offset long AFP_EndOff = 0x0; // sector end offset long AFP_FlashWidth = 0x8; // width of the flash device
int main(void) { char temp;
if (PowerOnInit()) { // open the device driver if (Flash_Open()) { AFP_Error = NO_ERR; } else { AFP_Error = PROCESS_COMMAND_ERR; }
//check the boot block lockout status Flash_GetBootBlockLockStatus(&temp); if (temp & 0x1) { //if D0 is high, the boot block is locked out return false; }
// allocate AFP_Buffer if ((AFP_Error = AllocateAFPBuffer()) != NO_ERR)
{ return false; } // get sector map if ((AFP_Error = GetSectorMap()) != NO_ERR)
{ return false; } // get the flash manufacturer & device codes, company name and description if ((AFP_Error = GetFlashInfo()) != NO_ERR)
{ return false; } // command processing loop while (!bExit) { // must have the lable "AFP_BreakReady" for the GUI to set breakpoint asm("AFP_BreakReady: nop;"); // the jump is used so that the label will be part of the debug // information in the driver image otherwise it may be left out // since the label is not referenced anywhere if (false) { asm("jump AFP_BreakReady;"); }
// Make a call to the ProcessCommand AFP_Error = ProcessCommand(); }
// Clear the AFP_Buffer FreeAFPBuffer();
// Close the Device if (Flash_Close()) { return true; } else { return false; } } else { return false; } }
/******************************************************************************************* * Function: ProcessCommand * * Desc: This function processes each AFP_Command sent by the Flash Programmer GUI * * Params: None * * Returns: ERROR_CODE - value if any error occurs during Opcode scan * NO_ERR - otherwise * * Notes: None *******************************************************************************************/ ERROR_CODE ProcessCommand(void) { ERROR_CODE ErrorCode = NO_ERR;
// switch on the command switch ( AFP_Command ) { case ERASE_ALL: // erase all if (Flash_EraseChip()) { ErrorCode = NO_ERR; } else { ErrorCode = POLL_TIMEOUT; } break;
case ERASE_SECT: // erase sector if (Flash_EraseSector(AFP_Sector)) { ErrorCode = NO_ERR; } else { ErrorCode = POLL_TIMEOUT; } break;
case FILL: // fill ErrorCode = FillData( AFP_Offset, AFP_Count, AFP_Stride, AFP_Buffer ); break;
case GET_CODES: // get manufacturer and device codes if (Flash_GetCodes(&AFP_ManCode, &AFP_DevCode)) { ErrorCode = NO_ERR; } else { ErrorCode = INVALID_SECTOR; } break;
case GET_SECTNUM: // get sector number based on address if (Flash_GetSectorNumber(AFP_Offset, &AFP_Sector)) { ErrorCode = NO_ERR; } else { ErrorCode = INVALID_SECTOR; } break;
case GET_SECSTARTEND: // get sector start and end addresses based on number if (Flash_GetSectorStartEnd(AFP_Sector, &AFP_StartOff, &AFP_EndOff)) { ErrorCode = NO_ERR; } else { ErrorCode = INVALID_SECTOR; } break;
case READ: // read ErrorCode = ReadData( AFP_Offset, AFP_Count, AFP_Stride, AFP_Buffer ); break;
case RESET: // reset if (Flash_Reset()) { ErrorCode = NO_ERR; } else { ErrorCode = POLL_TIMEOUT; } break;
case WRITE: // write ErrorCode = WriteData( AFP_Offset, AFP_Count, AFP_Stride, AFP_Buffer ); break;
// no command or unknown command do nothing case NO_COMMAND: default: ErrorCode = UNKNOWN_COMMAND; break; }
// clear the command AFP_Command = NO_COMMAND;
return ErrorCode; } 3.2 The Lower Layer
The lower layer driver provides hardware access functions to the physical device, such as erase, read, write functions. Refer to Flash Driver section for description and the project source code is available for download in the last section.
4 How to Debug the Flash Driver There are two parts of code that are debugged differently. For the functions that are called before AFP_BreakReady label, you can step through each function and debug as the normal way that you debug your application code. For the functions that are called in ProcessCommand(), since the commands come from the Flash Programmer in execution, the command is 0 (NO_COMMAND) in debug mode, but you can set the command value manually. Here are the steps.
• Open a VisualDSP++ memory window, browse to the AFP_Command symbol. You shall see all the global variables in the window.
Figure 1 Flash Programmer Global Variables List • Open the Flash Programmer window and load the newly built driver.  Figure 2 Flash Programmer Window - Driver Tab • Execute any of the available commands, such as Fill flash, Erase all, Erase sector, Program, etc. After the command is executed, all the global variables will be set up exactly as they normally would, except AFP_Command will be set to NO_COMMAND, and the execution stops at the AFP_BreakReady label even though it is not highlighted. But if you click Step Over or press F10, you’ll see the execution goes to one line right after the label.
 Figure 3 Flash Programmer Window - Commands Tab • Step into ProcessCommand () function, and then change the value of AFP_Command to the same value according to the AFP_CMD_TYPE enum definition as the command that was just executed through the Flash Programmer window. • Step through the code, the case/switch statement shall switch execution to the desired function where you can debug.
 Figure 4 Step into ProcessCommand() Function 5 How to Program Target Flash Once you have your target flash driver available, and your application loader file (app.ldr) ready, programming the flash on your target can be easily done with the following steps. • Click the menu Tools -> Flash Programmer…

Figure 5 Flash Programmer Menu • In the Flash Programmer window, click on Driver tab, Click Browse button to locate your flash driver executable (e.g. ES_AT49BV040BProgDrv.dxe), and then click Load Driver button, the flash driver shall be loaded. If the loading is successful, the flash information and driver information shall be shown in their respective box; the flash sector number, the start offset and end offset shall be shown in the Flash Sector Map box; and a message “Success: Driver loaded” shall be shown in the Message Center box. Check all the information to make sure they are right. Please refer to Figure 2 for example.
• Click on Programming tab, select the options in “Pre-program erase options” group and “File format” group. Then click Browse... button to locate and load your application loader file (app.ldr).
• Click Program button to start programming the loader file into the flash. Or click Compare button to compare the loader file with the image in the flash.
• Once the programming process is done, the memory usage percentage in each sector shall be shown in the Flash sector map box, and the erase success and program success messages shall be shown in the Message center box. See the example shown in Figure 6.
 Figure 6 Flash Erase and Programming Done Successfully
6 Source Code
The entire flash driver project source code is available for download here. ES_AT49BV040BProgDrv.zip
* Some notes in this article were referenced from Analog Devices ADSP-VisualDSP++ 4.5 Help contents. |