CrossStudio for ARM supports Flash programming (and subsequent debugging) by loading a program into the RAM of the target and transmitting it the data to be programmed.

The use of a target loader is determined by the value of the Loader File Path project property defined for the appropriate configuration of the project. The Loader File Path property specifies the location of the loader executable to use, if this is defined the loader executable will be downloaded onto the target an run prior to download of the main application.

In addition to the Loader File Path property, the Loader File Type project property must be specified. This tells CrossStudio how to communicate with the loader program. The various communication mechanisms available are explained in more detail later. The Load File Type property may be set to one of the following:

- Comms Channel Loader - The ARM debug comms channel is used to communicate with the loader.
- Fast Comms Channel Loader - The ARM debug comms channel is used to communicate with the loader. This scheme is significantly faster at downloading than Comms Channel Loader because it makes the assumption that the loader program is always ready to read data and therefore does not check the ARM comms channel status before transmitting data. This may not be suitable for all targets or loaders. If you experience reliability problems downloading and verifying programs using this setting, you should revert to the Comms Channel Loader setting.
- RAM Loader - The target's RAM is used to communicate with the loader.

The functionality a loader provides to CrossStudio is:

- Erase all memory.
- Erase length bytes starting at address.
- Write length bytes starting at address.
- Read length bytes starting at address.
- Set length bytes starting at address to value.

CrossStudio can communicate with the loader running on the ARM in one of two ways:

- ARM Debug Comms Port - All transactions with the loader are carried out over the ARM debug comms port. This is generally quicker than using RAM communication, however the ARM debug comms port is not supported on all targets.
- RAM - All transactions with the loader are carried out by the host writing data to target RAM, executing code and then reading the results out of target RAM. This system has the advantage that it will run on all targets, however it is not necessarily as quick as using the ARM debug comms port and can be hard to use if RAM is scarce.

To simplify the creation of a new loader program, a number of files have been supplied in the target/loader directory:

- loader.h - This file contains prototypes for all the loader functions and a number of useful macros.
- loader_main.c - This file contains the main entry point of a loader. It handles the reading of commands from the host and calling the appropriate loader entry points.
- loader_comm.c & loader_ram.c - These files implement the ARM debug comms port and RAM communication mechanisms used by the loader. Each file implements a version of the waitForCommand, loaderReadWord and loaderWriteWord functions. A loader that uses the ARM debug comms port should link in loader_comm.c and a loader that uses RAM should link in loader_ram.c. A loader using loader_comm.c should have the Loader File Type project property set to either Comms Channel Loader or Fast Comms Channel Loader. A loader using loader_ram.c should have the Loader File Type project property set to RAM Loader.

In order to implement a loader, the following loader entry points should be implemented:

- void loaderBegin() - This function is called before the loader enters it main loop, it can be used to initialize the loader if required.
- void loaderEnd()- This function is called when the loader exits it main loop, it can be used to clean up after the loader if required.
- int loaderPoke(unsigned char *address, unsigned int length) - This function is called when the host requests a write to memory. The address parameter specifies the address to start writing to, the length parameter specifies the number of bytes to write. The data to write should be read from the host using the loaderReadWord function, the bytes are stored in each word in little endian order. A non-zero value should be returned on success.
- int loaderMemset(unsigned char *address, unsigned int length, unsigned char c) - This function is called when the host request memory to be set to particular value. The address parameter specifies the address to start writing to, the length parameter specifies the number of bytes to write and the c parameter specifies the value to write. A non-zero value should be returned on success.
- int loaderPeek(unsigned char *address, unsigned int length) - This function is called when the host requests a read from memory. The address parameter specifies the address to start reading from, the length parameter specifies the number of bytes to read. The data read should be sent to the host using the loaderWriteWord function, the bytes are stored in each word in little endian order. A non-zero value should be returned on success.
- int loaderVerify(unsigned char *address, unsigned int length) - This function is called when the host requests memory to be verified. The address parameter specifies the address to start reading from, the length parameter specifies the number of bytes to verify. The data to verify should be read from the host using the loaderReadWord function, the bytes are stored in each word in little endian order. A non-zero value should be returned if all the data read matches the contents of memory. All bytes should be read from the host, even if verification fails.
- int loaderErase(unsigned char *address, unsigned int length) - This function is called when the host requests a block of non-volatile memory to be erased. The address parameter specifies the starting address of the block to erase, the length parameter specifies the length of the block in bytes. A non-zero value should be returned on success.
- int loaderEraseAll() - This function is called when the host requests all non-volatile memory to be erased. A non-zero value should be returned on success.
- int loaderSetParameter(unsigned int parameter, unsigned int value) - This function is called when the host attempts to set a loader specific property. The parameter parameter specifies the parameter to set, this is currently always set to zero. The value parameter specifies the value being set. The parameter value to be passed to the loader can be specified in the Loader Parameter project property.

A loader that uses loader_ram.c must also define the program section in RAM called .comm_buffer. The RAM this section occupies is used to write the data sent to and from the loader. The size to set the .comm_buffer section to is dependent on how much RAM you have free, however the larger you set the .comm_buffer the faster the loader will run.

The loader projects and source code for all the supported targets can be found in the target-specific directories contained in the targets directory

The following code demonstrates the structure of a loader implementation:

#include "../loader/loader.h"
void
loaderBegin()
{
}
void
loaderEnd()
{
}
int
loaderPoke(unsigned char *address, unsigned int length)
{
while (length)
{
unsigned int data = loaderReadWord();
int i;
for (i = 4; i && length; --i)
{
if (ADDRESS_IN_FLASH(address))
flash_write_byte(address++, (unsigned char)data);
else
*address++ = (unsigned char)data;
data >>= 8;
length--;
}
}
return 1;
}
int
loaderPeek(unsigned char *address, unsigned int length)
{
unsigned int data;
while (length)
{
int i;
for(i = 4; i; --i)
{
data >>= 8;
if (length)
{
data |= *address++ << 24;
--length;
}
}
loaderWriteWord(data);
}
return 1;
}
int
loaderMemset(unsigned char *address, unsigned int length, unsigned char c)
{
while(length--)
{
if (ADDRESS_IN_FLASH(address))
flash_write_byte(address++, (unsigned char)c);
else
*address++ = (unsigned char)c;
}
return 1;
}
int
loaderVerify(unsigned char *address, unsigned int length)
{
int result = 1;
while (length)
{
unsigned int data = loaderReadWord();
int i;
for (i = 4; i && length; --i)
{
if (*address++ != (unsigned char)data)
result = 0;
data >>= 8;
length--;
}
}
return result;
}
int
loaderErase(unsigned char *address, unsigned int length)
{
if (!is_erased(address, length))
flash_erase(address, length);
return 1;
}
int
loaderEraseAll()
{
if (!is_erased(FLASH_START_ADDRESS, FLASH_END_ADDRESS))
flash_erase_all(FLASH_START_ADDRESS);
return 1;
}

The targets directory contains a directory for each supported target. The loader source code for each target can be found in these directories. In order to view, edit and build a loader project open the Loader.hzp solution for the required target. By default CrossStudio picks the loaders from the Release/Loader.exe directory of each target directory.