ULP Advisor > Rule 5.3 Avoid processing-intensive operations: (s)printf()

ULPAdvisorBanner.PNG

ULP Advisor - Rule Table

ULP 1.1 Ensure LPM usage
ULP 2.1 Leverage timer module for delay loops
ULP 3.1 Use ISRs instead of flag polling
ULP 4.1 Terminate unused GPIOs
ULP 5.1 Avoid processing-intensive operations: modulo, divide.
ULP 5.2 Avoid processing-intensive operations: floating point
ULP 5.3 Avoid processing-intensive operations: (s)printf()
ULP 6.1 Avoid multiplication on devices without hardware multiplier
ULP 6.2 Use MATHLIB for complex math operations
ULP 6.3 Use Low Energy Accelerator (LEA) software library
ULP 7.1 Use local instead of global variables where possible
ULP 8.1 Use 'static' & 'const' modifiers for local variables
ULP 9.1 Use pass by reference for large variables
ULP 10.1 Minimize function calls from within ISRs
ULP 11.1 Use lower bits for loop program control flow
ULP 11.2 Use lower bits for port bit-banging
ULP 12.1 Use DMA for large memcpy() calls
ULP 12.1b Use DMA for potentially large memcpy() calls
ULP 12.2 Use DMA for repetitive transfer
ULP 13.1 Count down in loops
ULP 14.1 Use unsigned variables for indexing
ULP 15.1 Use bit-masks instead of bit-fields

Let us know what you think! Feedback, suggestions & comments
are welcome @ ULPAdvisorFeedback@list.ti.com

What it means

String format and printing functions such as (s)printf() are not natively supported on microcontroller architecture. Hence the use of these functions require the compiler to pull in additional libraries to generate a large amount of code. This additional code execution is directly translated into added time as well as energy required to perform the functions.

Risks, Severity

Depending on the support libraries and the compiler, (s)printf() functions usually require significant amount of code to be executed.

Why it is happening

The code contains print() or sprintf() function call(s).

Remedy

Avoid using (s)printf() functions in an MSP430 program. Try to print/send data in raw format if all possible. Alternatively, move the execution of the (s)printf() functions to RAM as code execution from RAM consumes much less power than Flash. If the MSP430 is part of a more complex system, try to offload the string printing/formatting to the host processor.

!Note that relocating the functions to RAM requires the use of a customized command linker file. This method is undetected by the ULP Advisor tool and the remark will still be issued. Once you can confirm the RAM execution in your code, you can suppress the remark by disabling the option for this rule in the Project's ULP Advisor setting.

Code Example

Note that this instruction is specific for CCS. 

The key idea behind RAM-executed functions is to link the functions to a memory section with the following attribute: "load = FLASH_AREA, run = RAM_AREA" defined in the linker command file. As the parameters suggest, the objects in the section will be allocated to FLASH, but at start up they will be copied over to RAM for execution. Note that the names FLASH_AREA or RAM_AREA can be the default names (FLASH, RAM) or preferrably user-defined to prevent confusion. For MSP430 devices with FRAM, FLASH regions can be substituted by FRAM regions.

The following steps describe necessary procedure to relocate functions to RAM for execution.

I. Create a load = FLASH, run = RAM memory section in the device linker command file.

  1. Reserve a memory region in FLASH to store the functions and a memory region in RAM for copy/execution.

Before:

 RAM : origin = 0x2400, length = 0x2000
 INFOA : origin = 0x1980, length = 0x0080
 INFOB : origin = 0x1900, length = 0x0080
 INFOC : origin = 0x1880, length = 0x0080
 INFOD : origin = 0x1800, length = 0x0080
 FLASH : origin = 0x4400, length = 0xBB80
 FLASH2 : origin = 0x10000,length = 0x14400
 ...

After reserving 0x200 bytes for RAM_EXECUTE and 0x200 bytes for FLASH_EXECUTE. Notice the new starting addresses (origins) & lengths:

 RAM_EXECUTE : origin = 0x2400, length = 0x0200
 RAM   : origin = 0x2600, length = 0x1C00
 INFOA : origin = 0x1980, length = 0x0080
 INFOB : origin = 0x1900, length = 0x0080
 INFOC : origin = 0x1880, length = 0x0080
 INFOD : origin = 0x1800, length = 0x0080
 FLASH_EXECUTE:origin = 0x4400, length = 0x0x200
 FLASH : origin = 0x4600, length = 0xB980
 FLASH2 : origin = 0x10000,length = 0x14400
 ...
  1.  Create a section to load from FLASH_EXECUTE & run from RAM_EXECUTE:
.bss : {} > RAM /* GLOBAL & STATIC VARS */
.sysmem : {} > RAM /* DYNAMIC MEMORY ALLOCATION AREA */
.stack : {} > RAM (HIGH) /* SOFTWARE SYSTEM STACK */
// add this line
.run_from_ram: load = FLASH_EXECUTE, run = RAM_EXECUTE
// 
.text : {}>> FLASH | FLASH2 /* CODE */
.text:_isr : {} > FLASH /* ISR CODE SPACE */
....

We're done with the linker command file modification at this point.


II. Relocate user-generated functions:

    1. Identify the functions to be relocated to RAM in your code.

    2. Before the definition of each function, add the following line


#pragma CODE_SECTION(FunctionName,".run_from_ram")
void FunctionName(void)
{


III. Relocate built-in run-time library (rts430*) functions:

If you use division, multiplication, or printf/sprintf in your applications, these functions are pulled in from the real-time library. These processor intensive functions should also be relocated to RAM to optimize your application for power.

You can find out whether your application uses many of these functions by looking at the output map file and searching for rts430*.lib. For example

rts430xl.lib : mult16_f5hw.obj (.text)
rts430xl.lib : div16u.obj (.text)
rts430xl.lib : sprintf.obj (.text:sprintf)
sprintf.obj (.text:_outs)
sprintf.obj (.text:_outc)

Add the following lines to the linker command file to redirect these objects into the FLASH_EXECUTE//RAM_EXECUTE area

Option 1 


.text:rts430.lib_div : { rts*.lib<div16u.obj>(.text) } load = FLASH_EXECUTE, run = RAM_EXECUTE
.text:rts430.lib_mult : { rts*.lib<mult16_f5hw.obj>(.text) } load = FLASH_EXECUTE, run = RAM_EXECUTE
.text:rts430.lib_printf : { rts*.lib<*printf.obj>(.text) } load = FLASH_EXECUTE, run = RAM_EXECUTE

Option 2, if you have defined the .run_from_ram section 


.text:rts430.lib_div : { rts*.lib<div16u.obj>(.text) } .run_from_ram 
.text:rts430.lib_mult : { rts*.lib<mult16_f5hw.obj>(.text) } .run_from_ram
.text:rts430.lib_printf : { rts*.lib<*printf.obj>(.text) } .run_from_ram


Refer to File:RAM Function Example.zip for an example in CCS on how to move a function to RAM for execution.

In IAR, the keyword __ramfunc can be placed in front of the function declaration to enable the function be copied from Flash to RAM during startup and the function will be referenced from the rest of the code in RAM.

See the rest of the code examples for all MSP430 devices here!

More Resources

Want to squeeze a few more nanoAmps out of your application? Leverage the e2e (Engineer-to-Engineer) online community to get all of your ULP questions answered! Or, if you are an Ultra-Low Power pro, give back to the community with your expertise.

Go to MSP430's e2e online forum! 

If you are posting on the forums in relation to this rule, try using the tag "ULP_5.3"

E2e.jpg For technical support please post your questions at http://e2e.ti.com. Please post only comments about the article Compiler/diagnostic messages/MSP430/1532 here.