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.