<< Previous | Index | Next >> | |
|
This chapter offers hints on how to speed up an application and how to store persistent data at run time.
17.1 Efficiency
There are a number of methods that can be used to reduce the size of a program, or to increase its speed. Let's look at the events that occur when a program enters a function.
The function saves IX on the stack and makes IX the stack frame reference pointer (if the program is in the
useix
mode).The function sets up stack corruption checks if stack checking is enabled (on).
The program notifies Dynamic C of the entry to the function so that single stepping modes can be resolved (if in debug mode).
The last two consume significant execution time and are eliminated when stack checking is disabled or if the debug mode is off.
17.1.1 Nodebug Keyword
When the PC is connected to a target controller with Dynamic C running, the normal code and debugging features are enabled. Dynamic C places an
RST
28H
instruction at the beginning of each C statement to provide locations for breakpoints. This allows the programmer to single step through the program or to set breakpoints. (It is possible to single step through assembly code at any time.) During debugging there is additional overhead for entry and exit bookkeeping, and for checking array bounds, stack corruption, and pointer stores. These "jumps" to the debugger consume one byte of code space and also require execution time for each statement.At some point, the Dynamic C program will be debugged and can run on the target controller without the Dynamic C debugger. This saves on overhead when the program is executing. The
nodebug
keyword is used in the function declaration to remove the extra debugging instructions and checks.nodebug int myfunc( int x, int z ){
...
}If programs are executing on the target controller with the debugging instructions present, but without Dynamic C attached, the function that handles
RST
28H
instructions will be replaced by a simpleret
instruction. The target controller will work, but its performance will not be as good as when thenodebug
keyword is used.If the
nodebug
option is used for themain
function, the program will begin to execute as soon as it finishes compiling (as long as the program is not compiling to a file).Use the directive
#nodebug
anywhere within the program to enablenodebug
for all statements following the directive. The#debug
directive has the opposite effect.Assembly code blocks are nodebug by default, even when they occur inside C functions that are marked debug, therefore using the
nodebug
keyword with the#asm
directive is usually unnecessary.17.1.2 Static Variables
Using
static
variables withnodebug
functions will greatly increase the program speed. Stack checking is disabled by default.When there are more than 128 bytes of auto variables declared in a function, the first 128 bytes are more easily accessed than later declarations because of the limited 8-bit range of IX and SP register addressing. This makes performance slower for bytes above 128.
The
shared
and theprotected
keywords in data declarations cause slower fetches and stores, except for one-byte items and some two-byte items.17.2 Run-time Storage of Data
Data that will never change in a program can be put in flash by initializing it in the declarations. The compiler will put this data in flash. See the description of the
const
,xdata
, andxstring
keywords for more information.If data must be stored at run-time and persist between power cycles, there are several ways to do this using Dynamic C functions:
User Block - Recommended method for storing non-file data. This is where calibration constants for boards with analog I/O are stored in the factory. Space here is limited to as small as
8K-sizeof(SysIDBlock)
bytes, or less if there are calibration constants.Flash File System - The file system is best for storing data that must be organized into files, or data that won't fit in the User block. It is best used on a second flash chip. It is not possible to use a second flash for both extra program code that doesn't fit into the first flash, and the file system. The macro
USE_2NDFLASH_CODE
must be uncommented in the BIOS to allow programs to grow into the second flash; this precludes the use of the file system.WriteFlash2 - This function is provided for writing arbitrary amounts of data directly to arbitrary addresses in the second flash.
Battery-Backed RAM - Storing data here is as easy as assigning values to global variables or local static variables. The file system can also be configured to use RAM. The important question is, what will you do when your battery runs out?
17.2.1 User Block
The User block is an area near the top of flash reserved for run-time storage of persistent data and calibration constants. The size of the User block can be read in the global structure member
SysIDBlock.
userBlockSize. The functions readUserBlock() and writeUserBlock() are used to access the User block. These function take an offset into the block as a parameter. The highest offset available to the user in the User block will be
SysIDBlock.
userBlockSize-1if there are no calibration constants, or
DAC_CALIB_ADDR-1
See the Rabbit 3000 Designer's Handbook or the Rabbit 2000 Designer's Handbook for more details about the User block.
17.2.2 Flash File System
For a complete discussion of the file system, please see The Flash File System.
17.2.3 WriteFlash2
See the Dynamic C Function Reference Manual for a complete description.
NOTE There is a WriteFlash()
function available for writing to the first flash, but its use is highly discouraged for reasons of forward source and binary compatibility should flash sector configuration change drastically in a product. See Technical Notes 216 and 217 for more information on flash compatibility issues.17.2.4 Battery Backed RAM
Static variables and global variables will always be located at the same addresses between power cycles and can only change locations via recompilation. The file system can be configured to use RAM also. While there may applications where storing persistent in RAM is acceptable, for example a data logger where the data gets retrieved and the battery checked periodically, keep in mind that a programming error such as an uninitialized pointer could cause RAM data to be corrupted.
xalloc()
will allocate blocks of RAM in extended memory. It will allocate the blocks consistently from the same physical address if done at the beginning of the program and the program is not recompiled.17.3 Root Memory Reduction Tips
Customers with programs that are near the limits of root code and/or root data space usage will be interested in these tips for saving root space. The usage of root code and data by the BIOS in Dynamic C 7.20 increased from previous versions. A follow-on release will reduce BIOS root space usage, but probably not to the level of usage in previous versions.
17.3.1 Increasing Root Code Space
Increasing the available amount of root code space may be done in the following ways:
This will cause C functions that are not explicitly declared as "root" to be placed in xmem. Note that the only reason to locate a C function in root is because it modifies the XPC register (in embedded assembly code), or it is an ISR. The only performance difference in running code in xmem is in getting there and returning. It takes a total of 12 additional machine cycles because of the differences between call/lcall, and ret/lret.
Reduce usage of root constants and string literals
Shortening literal strings and reusing them will save root space. The compiler, starting with version 7.20, automatically reuses identical string literals.
These two statements :
printf ("This is a literal string");
sprintf (buf, "This is a literal string");will share the same literal string space whereas:
sprintf (buf, "this is a literal string");
will use its own space since the string is different.
Use xdata to declare large tables of initialized data
If you have large tables of initialized data, consider using the keyword xdata to declare them. The disadvantage is that data cannot be accessed directly with pointers. The function xmem2root() allows xdata to be copied to a root buffer when needed.
// This uses root code space
const int root_tbl[8]={300,301,302,103,304,305,306,307};// This does not
xdata xdata_table {300,301,302,103,304,305,306,307};main(){
// this only uses temporary stack space
auto int table[8];xmem2root(table, xdata_table, 16);
// now the xmem data can be accessed via a 16 bit pointer into the table
}Both methods, const and xdata, create initialized data in flash at compile time, so the data cannot be rewritten directly.
Use xstring to declare a table of strings
The keyword xstring declares a table of strings in extended flash memory. The disadvantage is that the strings cannot be accessed directly with pointers, since the table entries are 20-bit physical addresses. As illustrated above, the function xmem2root() may be used to store the table in temporary stack space.
// This uses root code space
const char * name[] = {"string_1", . . . "string_n"};// This does not
xstring name {"string_1", . . . "string_n"};Both methods, const and xstring, create initialized data in flash at compile time, so the data cannot be rewritten directly.
Place assembly language code into xmem
Pure assembly language code functions can go into xmem starting with Dynamic C 7.20:
#asm
foo_root::
[some instructions]
ret
#endasmThe same function in xmem:
#asm xmem
foo_xmem::
[some instructions]
lret ; use lret instead of ret
#endasmThe correct calls are call foo_root and lcall foo_xmem. If the assembly function modifies the XPC register with
LD XPC, A
it should not be placed in xmem. If it accesses data on the stack directly, the data will be one byte away from where it would be with a root function because lcall pushes the value of XPC onto the stack.
17.3.2 Increasing Root Data Space
Increasing the available amount of root data space may be done in the following ways:
The default storage class of Dynamic C is static. This can be changed to auto using the directive #class auto. This will make local variables with no explicit storage class specified in functions default to auto. If you need the value in a local function to be retained between calls, it should be static. The default program stack size is 2048 (0x800) bytes if not using µC/OS-II. This could be increased to 0x1000 at most. It already is increased if the TCP/IP stack is used. The code to change it is in program.lib:
#ifndef MCOS
#define DEFAULTSTACKSIZE 0x1000 ; increased from 0x800
#else
#define DEFAULTSTACKSIZE 0x200
#endifDeeply nested calls with a lot of local auto arrays could exceed this limit, but 0x1000 should ordinarily be plenty of space. Using more temporary stack space for variables frees up static root data space for global and local static variables.
Use xmem for large RAM buffers
xalloc() can be used to allocate chunks of RAM in extended memory. The memory cannot be accessed by a 16 bit pointer, so using it can be more difficult. The functions xmem2root() and root2xmem() are available for moving from root to xmem and xmem to root. Large buffers used by Dynamic C libraries are already allocated from RAM in extended memory.
| |
<< Previous | Index | Next >> | |
Z-World, Inc. www.zworld.com Phone: 1.530.757.3737 Fax: 1.530.757.3792 |
Rabbit Semiconductor www.rabbitsemiconductor.com Phone: 1.530.757.8400 Fax: 1.530.757.8402 |