Home > Developer > Mathematical Function in BREW

SophiaFramework : BREW  C++ Class Library & GUI Framework & XML Middleware

Mathematical Function in BREW

Case

Standard floating point operations cannot be executed on BREW enabled devices. BREW helper functions must be substituted for floating-point operations.

To make things worst, BREW does not support the float type, only double, and most mathematical functions are not available either.

Cause

When compiling BREW SoftFP operations, the ARM compiler will convert all floating point operations to ARM compiler functions.

These ARM compiler functions will link to other libraries, some of them using static variables. However, BREW does not allow static variables.

And, since BREW applications do not have main() functions, initialization for the standard library will not be done automatically

Goal

The goal is to use standard floating point operations with BREW.

float a = 1.0;
float b = 2.0;
float c;
int i;

c = sin(a) + cos(b);
i = c * 100.05;

Solution

The ARM compiler will replace floating point operations by the following ARM compiler functions defined in its attached rt_fp.h header file.

/*
 * Single-precision arithmetic routines.
 */
extern __softfp float _fadd(float, float);
extern __softfp float _fsub(float, float);
extern __softfp float _fmul(float, float);
extern __softfp float _fdiv(float, float);
...etc.

To use these ARM compiler functions, _fp_init() needs to be called from inside AEEMod_Load() which boots up the application.

/*
 * Since BREW has no main function, 
 * fplib must be initialized manually with _fp_init()
 */
extern void _fp_init(void);

After calling the _fp_init() function, several ARM compiler functions can be linked. But BREW is not capable of using all these functions. Specifically, functions with static variables and SWI instructions can not be used in BREW.

<rt_misc.h>

/*
 * The library's signal handling process must be customized for BREW.
 * __raise(), which traps error signals, cannot be called in BREW. 
 * So the functions that calls it, __rt_raise() is re-targeted.
*/ 
void __rt_raise(int /*sig*/, int /*type*/);

<errno.h>
extern __pure volatile int *__rt_errno_addr(void);

<rt_fp.h>
/*
 * This returns a pointer to the FP status word, when it's stored
 * in memory.
 */
extern unsigned *__rt_fp_status_addr(void);

There are two ways to replace these incompatible functions, by using C or by using assembler. ( The C method is recommended )

The address of the handset's variable pool must be returned to the two functions above. The method of implementation will change according to the position of the variable pool.

Re-Targeting the __rt_raise Function with C language

In this method, the variable pool is reserved in the AEEApplet structure, and the function is implemented to return that address.

When that function is called, the GETAPPINSTANCE() helper function is called to aquire the address of the AEEApplet structure.

#if defined __arm
#include <rt_misc.h >
#include <errno.h >
#include <rt_fp.h >
#endif

#if defined __arm
#pragma import(__use_no_semihosting_swi)
#endif

typedef struct SoftFPApplet {
    AEEApplet aee;
#if defined __arm
    signed int volatile _errno;
    unsigned int _fpstatus;
#endif
} SoftFPApplet;

#if defined __arm
void __rt_raise(signed int signal, signed int type)
{
    return;
}

signed int volatile* __rt_errno_addr(void)
{
    SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE();

    return(&ap->_errno);
}

unsigned int* __rt_fp_status_addr(void)
{
    SoftFPApplet* ap = (SoftFPApplet*)GETAPPINSTANCE();

    return(&ap->_fpstatus);
}
#endif

int AEEClsCreateInstance(AEECLSID ClsId,
IShell * pIShell,IModule * po,void ** ppObj)
{
    *ppObj = NULL;

    if (ClsId == AEECLSID_SOFTFP) {
        if (AEEApplet_New(sizeof(SoftFPApplet), 
            ClsId, pIShell, po, (IApplet**)ppObj,
            (AEEHANDLER)SoftFP_HandleEvent, NULL) == TRUE) {

#if defined __arm
            _fp_init();
#endif

            // Add your code here .....

            return(AEE_SUCCESS);
        }
    }
    return(EFAILED);
}
Re-Targeting the __rt_raise Function By Assembler

In the assembler method, the variable pool uses a particle of the application module itself as the static variable area. The problem with this method is that basic BREW applications are ROPI (Read Only Position Independent). If the application is embedded in the ROM memory, this method will no longer be valid.

This method may not be applicable in some BREW enabled handsets.

    AREA ||i.__rt_raise||, CODE, READONLY

    EXPORT __rt_raise

__rt_raise
    BX          lr


    AREA ||i.__rt_errno_addr||, CODE, READONLY

    EXPORT __rt_errno_addr

__rt_errno_addr
    LDR         r0, table_errno_addr
    ADD         r0, pc, r0
    BX          lr
table_errno_addr
    DCD         errno_addr - {PC}


    AREA ||i.__rt_fp_status_addr||, CODE, READONLY

    EXPORT __rt_fp_status_addr

__rt_fp_status_addr
    LDR         r0, table_status_addr
    ADD         r0, pc, r0
    BX          lr
table_status_addr
    DCD         status_addr - {PC}


    AREA ||.constdata||, DATA, READONLY

errno_addr
    DCD         0x00000000
status_addr
    DCD         0x00000000

    END

By implementing these methods,standard floating point operations can be used with BREW.

SophiaFramework comes equipped with standard floating-point operations by default.