> ## 17. Hints and Tips

> 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 creates stack space for `auto` variables.

> - 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 simple `ret` instruction. The target controller will work, but its performance will not be as good as when the `nodebug` keyword is used.

> If the `nodebug` option is used for the `main` 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 enable `nodebug` 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 with `nodebug` 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 the `protected` 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 Persistent 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`, and `xstring` 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 flash 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 Flash File System. The macro `USE_2NDFLASH_CODE` must be uncommented to allow programs to grow into the second flash and this precludes 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-1

> if there are no calibration constants, or

>     DAC_CALIB_ADDR-1

> if there are.

> 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 drasticly in a product. SeeTechnical 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 Usage 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 Available Root Code Space

> Increasing the available amount of root code space may be done in the following ways:

> - Use #memmap xmem

>   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.

> - Increase DATAORG

>   Root code space can be increased by increasing DATAORG in RabbitBios.c in increments of 0x1000. Unfortunately, this comes at the expense of root data space, but there are ways of reducing that too.

> - 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_table[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.

> - Turn off selected debugging features

>   In Dynamic C 7.20 , watch expressions, breakpoints, and single-stepping can be selectively disabled in the Options | Debugging dialog to save some root code space.

> - 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
>       #endasm

>   The same function in xmem:

>       #asm xmem
>       foo_xmem::
>          [some instructions]
>          lret     ; use lret instead of ret
>       #endasm

>   The 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 Available Root Data Space

> Increasing the available amount of root data space may be done in the following ways:

> - Decrease DATAORG

>   Root data space can be increased by decreasing DATAORG in RabbitBios.c in increments of 0x1000. This comes at the expense of root code space.

> - Use #class auto

>   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
>       #endif

>   Deeply 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.

Z-World http://www.zworld.com Voice: (530) 757-3737 FAX: (530) 757-3792 sales@zworld.com