<< Previous | Index | Next >>

> ## 19. Hints and Tips

> This chapter offers hints on how to speed up an application and how to store persistent data at run time.

> ### 19.1 A User-Defined BIOS

> The file `RabbitBIOS.c` is a wrapper that permits a choice of which BIOS to compile. A modular design has many of the configuration macros in separate configuration libraries. The BIOS file and configuration libraries are located in `LIB\BIOSLIB`. Table 19-1 lists the new files and gives a brief description of their content.

> Table 19-1. BIOS File and Configuration Libraries
> File Name | Description
> STDBIOS.C | Most of the code from `RabbitBIOS.c` was moved here.
> CLONECONFIG.LIB | Macros for configuring cloning.
> DKCONFIG.LIB | Macros for configuring the debug kernel
> ERRLOGCONFIG.LIB | Macros for configuring non-RabbitSys error logging. RabbitSys has its own error logging method.
> MEMCONFIG.LIB | Macros for configuring memory organization.
> SYSCONFIG.LIB | Macros for other system-level configuration options, such as the clock doubler and the specturm spreader.
> TWOPROGRAMCONFIG.LIB | Macros for configuring split memory for the old-style DLM/DLP.
> FATCONFIG.LIB | Macros for configuring the FAT file system.

> To create a user-defined BIOS, begin with a copy of `STDBIOS.C` to modify. It is prudent to save `STDBIOS.C` as is and rename the modified file.

> The Dynamic C GUI offers an option for hooking a user-defined BIOS into the system. See the description of the "Advanced... Button" in Section 16.2.7 for details on using this GUI option. You will need to consider the configuration files and associated macros described in Table 19-1.

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

> #### 19.2.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 call to the function that handles RST 28H instructions in the vector table will be treated as a NOP by the processor when in debug mode. 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.

> #### 19.2.2 In-line I/O

> The built-in I/O functions (`WrPortI()`, `RdPortI()`, `BitWrPortI()` and `BitRdPortI()`) can be generated as efficient in-line code instead of function calls. All arguments must be constant. A normal function call is generated if the I/O function is called with any non-constant arguments. To enable in-line code generation for the built-in I/O functions check the option "Inline builtin I/O functions" in the Compiler dialog, which is accessible by clicking the Compiler tab in the Project Options dialog.

> ### 19.3 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`, 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. Factory-stored calibration constants live in the User block for boards with analog I/O. Space here is limited to as small as (`8K-sizeof(SysIDBlock))` bytes, or less if there are calibration constants. For specific information about the User block on your board, open the sample programs `userblock_info.c` and/or `idblock_report.c`. The latter program will print, among other things, the location of the User block.

> - 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 life of a battery on a Rabbit board is specified in the user's manual for that board; some boards have batteries that last several years, most board have batteries that come close to or surpass the shelf-life of the battery. If it is important that battery-backed data not be lost during a power failure, know how long your battery will last and plan accordingly.

> #### 19.3.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 designer's handbook for more details about the User block.

> #### 19.3.2 WriteFlash2

> See the Dynamic C Function Reference Manual for a complete description of `WriteFlash2()`.

> There is a function available for writing to the first flash, `WriteFlash()`, but its use is highly discouraged for reasons of forward source and binary compatibility should flash sector configuration change drastically in a product. For more information on flash compatibility issues, see technical notes TN216 "Is your Application Ready for Large Sector Flash?" and TN217 "Binary and Source Compatibility Issues for 4K Flash Sector Sizes" at Rabbit's website: rabbit.com..

> #### 19.3.3 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 be applications where storing persistent data 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.

> ### 19.4 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. For more help, see Technical Note TN238 "Rabbit Memory Usage Tips." This document is available at: rabbit.com, or by choosing Online Documentation from within the Help menu of Dynamic C.

> #### 19.4.1 Increasing Root Code Space

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

> - Enable Separate Instruction and Data Space

>   A hardware memory management scheme that uses address line inversion to double the amount of logical address space in the base and data segments is enabled on the Compiler tab of the Options | Project Options dialog. Enabling separate I&D space doubles the amount of root cod and root data available for an application program.

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

>   The macro `ROOT_SIZE_4K` determines the beginning logical address for the data segment.

>   Root code space can be increased by increasing `ROOT_SIZE_4K` in StdBIOS.c. The default is 0x3 when separate I&D space is on, and 0x6 otherwise. It can be as high as 0xB.

>   When separate I&D space is on, `ROOT_SIZE_4K` defines the boundary between root variable data and root constant data. In this case, increasing `ROOT_SIZE_4K` increases root constant space and decreases root variable space.

>   When separate I&D space is off, `ROOT_SIZE_4K` determiness the boundary between root variable data and the combination of root code and root constant data. Note that root constants are in the base segment with root code. In this case, increasing `ROOT_SIZE_4K` increases root code and root constant space and decreases root data space.

> - Compile out floating point support

>   Floating point support can be conditionally compiled out of `stdio.lib` by adding `#define STDIO_DISABLE_FLOATS` to either a user program or the Defines tab page in the Project Options dialog. This can save several thousand bytes of code space.

> - Reduce usage of root constants and string literals

>   Shortening literal strings and reusing them will save root space. The compiler 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 the far keyword to directly declare variables in xmem

>   See Section 4.16 and Chapter 14 for more information on the far keyword.

> - Turn off selected debugging features

>   Watch expressions, breakpoints, and single stepping can be selectively disabled on the Debugger tab of Project Options to save some root code space.

> - Place assembly language code into xmem

>   Pure assembly language code functions can go into xmem.

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

> #### 19.4.2 Increasing Root Data Space

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

> - Enable Separate Instruction and Data Space

>   A hardware memory management scheme that uses address line inversion to double the amount of logical address space in the base and data segments is enabled on the Compiler tab of the Options | Project Options dialog. Enabling separate I&D space doubles the amount of root code and root data available for an application program.

> - Decrease ROOT_SIZE_4K2

>   The macro `ROOT_SIZE_4K` determines the beginning logical address for the data segment.

>   Root data space can be increased by decreasing `ROOT_SIZE_4K` in StdBIOS.c. At the time of this writing, RAM compiles should be done with no less than the default value (0x6) of `DATAORG` when separate I&D space is off. This restriction is to ensure that the pilot BIOS does not overwrite itself.

>   When separate I&D space is on, `ROOT_SIZE_4K` determines the boundary between root variable data and root constant data. In this case, decreasing `ROOT_SIZE_4K` increases root variable space and descreases root constant space.

>   When separate I&D space is off, `ROOT_SIZE_4K` determines the boundary between root variable data and the combination of root code and root constant data. Note that root constants are in the base segment with root code. In this case, decreasing `ROOT_SIZE_4K` increases root data space and decreases root code space.

> - Use xmem for large RAM buffers

>   `xalloc()` can be used to allocate chunks of RAM in extended memory (xmem) but its use is no longer necessary for data objects which exist for the program's lifetime. It is, however, preserved for backwards compatibility.

>   Using the far keyword is easier and more efficient than using `xalloc()`. Consider the following code:

>       far char my_buffer[BUFFER_SIZE];

>       int main() {
>          far char *p;
>          p = my_buffer;
>          ...               // access xmem directly
>       }

>                Large buffers used by Dynamic C libraries are already allocated from RAM in xmem.

> 1 The macro DATAORG was deprecated in favor of ROOT_SIZE_4K starting with Dynamic C 10.21. ROOT_SIZE_4K equals DATAORG/0x1000.
> 2 The macro DATAORG was deprecated in favor of ROOT_SIZE_4K starting with Dynamic C 10.21. ROOT_SIZE_4K equals DATAORG/0x1000.

Dynamic C User's Manual | << Previous | Index | Next>> | rabbit.com