Writing optimized c code for microcontroller applications

21 443 0
Writing optimized c code for microcontroller applications

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

WRITING OPTIMIZED C CODE FOR MICROCONTROLLER APPLICATIONS By Wilson Chan Toshiba America Electronics Components, Inc Email: wilson.chan@taec.toshiba.com INTRODUCTION If you have a microcontroller project that requires a small program, or the application has very limited memory resource, you may prefer to use Assembly language for programming Nowadays, as the performance of microcontrollers has been improving, application systems have become larger and more complicated As a result, programs can no longer be coded in Assembly language easily To improve development efficiency, many microcontroller based products are programmed in C Generally, when programs are written in C and compiled by a C compiler, the code efficiency decreases compared to an Assembly language program In order to improve code efficiency, most C compilers make use of optimization techniques Often, the output code is optimized for size, or execution speed, or both Besides relying on the C compiler to generate efficient code, the programmer can lend a helping hand to the C compiler by adopting certain programming styles This paper provides an overview of common optimizing techniques used by C compilers and recommend C programming guidelines that will result in optimized code for microcontroller applications PROGRAMMING MODEL Some microcontrollers not have hardware support for a C stack If you plan to develop your embedded applications in C, you should select a microcontroller with a stack-based architecture If the microcontroller has dedicated address-specifying/index registers, they will also help the C compiler to generate more efficient code In this paper, we’ll use a C compiler for a microcontroller which has a programming model as shown in Figure to illustrate the effect of various optimization methods on the quality of the generated machine code The W, A, B, C, D, E, H, L registers are 8-bit general purpose registers They can be used in pairs as four 16-bit general purpose registers: WA, BC, DE and HL The IX and IY registers are specialpurpose 16-bit registers used as address-specifying registers under register indirect addressing mode and as index registers under index addressing mode The SP register is a 16-bit stack pointer The PC register is a 16-bit program counter The PSW is a 16-bit program status word register JF is the jump status flag, ZF is the zero flag, CF is the carry flag, HF is the half carry flag, SF is the sign flag, and VF is the overflow flag W B D H A C E L IX IY SP PC PSW General-Purpose : Special-Purpose : 8bit X 16bit (IX, IY, SP, PC) PSW = JF, ZF, CF, HF, SF, VF Figure Programming Model COMMON OPTIMIZATIONS IN C COMPILERS This section describes common optimization techniques often found in optimizing C compilers Convolution and Propagation of Constants This optimization method propagates constants in place of variables whenever possible and computes any constant expression at compile time rather than at run time Consider the C program example in Figure For the C statement in line 3, the C compiler computes the addition of two constants at compile time rather than at execution time Similarly, for C statements from lines to 6, the constant in line is propagated to lines and This optimization technique reduces program size and increases execution speed C Language Program int i, a, b, c; test(){ i = + 2; a = 1; b = a + 2; c = a + b; } Without Optimization _test: ld ld add ld ld ld ld inc 10 inc 11 ld 12 ld 13 ld 14 add 15 ld 16 ret WA,0x1 BC,0x2 WA,BC (_i),WA WA,0x1 IY,_a (IY),WA WA WA IX,_b (IX),WA WA,(IY) WA,(IX) (_c),WA With Optimization _test: ld ld ld ld ld ld ld ld 10 ret Figure Convolution and Propagation of Constants WA,0x3 (_i),WA WA,0x1 (_a),WA WA,0x3 (_b),WA WA,0x4 (_c),WA Dead-Code Elimination This optimization method deletes unused variables at compile time Consider the C program example in Figure With dead-code elimination optimization, the C compiler eliminates the C statement in line C Language Program int test(){ int a; a = 1; return 0; } Without Optimization _test: ld xor ret WA,0x1 WA,WA Figure Dead-Code Elimination With Optimization _test: xor ret WA,WA Strength Reduction This optimization method replaces expensive operations with less expensive ones Consider the C program example in Figure The most efficient code is a left-shift instead of an integer multiplication Without optimization, the generated code makes a call to a multiplication function supplied by the C run-time library to compute the multiplication which takes much longer than a left-shift operation C Language Program int i; test() { i *= 2; } Without Optimization _test: ld ld cal ld ret BC,0x2 WA,(_i) C87C_muli (_i),WA Figure Strength Reduction With Optimization _test: ld ld shlca ld ret IY,_i WA,(IY) WA (IY),WA Common Sub-Expression Elimination This optimization method reduces the number of operations by using the first operation result in subsequent statements that contain the same operation Consider the C program example in Figure The calculation of the sub-expression, i + 1, is reduced from two times to once with optimization C Language Program int test(int *a, int *b, int i) { return(a[i+1] + b[i+1]); } Without Optimization _test: ld inc shlca ld add ld inc shlca 10 ld 11 add 12 ld 13 add 14 ret WA,(SP+0x7) WA WA IX,WA IX,(SP+0x3) WA,(SP+0x7) WA WA DE,WA DE,(SP+0x5) WA,(DE) WA,(IX) With Optimization _test: ld inc shlca ld add ld add ld 10 add 11 ret Figure Common Sub-Expression Elimination WA,(SP+0x7) WA WA IX,WA IX,(SP+0x3) DE,WA DE,(SP+0x5) WA,(DE) WA,(IX) Code Motion This optimization method is often used to optimize loops Generally speaking, most of the program execution time is spent in loops Therefore, it is important for C compilers to provide optimization for loops Consider the C program example in Figure First, the invariant operation, b + c, is moved outside of the loop Second, array address calculations that use an induced variable (updated at each iteration) are reduced to incrementing an accumulator The optimized code will not only be smaller in size (26 bytes versus 39 bytes), but will also execute much faster C Language Program int a[10], b, c; test(){ int i; for (i = 0; i < 10; i++) a[i] = b + c; } Without Optimization _test: ld cmp j L2: ld shlca ld add 10 ld 11 add 12 ld 13 inc 14 cmp 15 j 16 L1: 17 ret BC,0x0 BC,0xa sge,L1 WA,BC WA DE,WA DE,_a WA,(_b) WA,(_c) (DE),WA BC BC,0xa slt,L2 Figure Code Motion With Optimization _test: ld add ld ld add L2: ld inc 10 inc 11 cmp 12 j 13 ret BC,(_b) BC,(_c) WA,_a DE,WA WA,0x14 (DE),BC DE DE DE,WA lt,L2 Caching Of Memory Access This optimization method reduces the number of memory access, thus speeding up program execution Consider the C program example in Figure With optimization, the number of memory access is reduced from three to one Also, the resultant code occupies less memory (12 bytes versus 20 bytes) C Language Program int a; int test() { if (a != 1) return a; else return a - 1; } Without Optimization _test: ld cmp j ld ret L1: ld dec 10 ret WA,(_a) WA,0x1 t,L1 WA,(_a) WA,(_a) WA With Optimization _test: ld cmp j ret L1: dec ret WA,(_a) WA,0x1 t,L1 WA Figure Caching of Memory Access Switch Table Optimization Switch statements are very common in C programs This optimization calls for the C compiler to analyze the nature of the case values in the switch statement, then decide on the optimum way to implement the switch statement Consider the C program examples in Figure The C compiler implements the switch statement in the C program as a series of compare and branch instructions For the C program 2, the C Compiler uses a different coding style to improve code efficiency C Language Program C Language Program unsigned char j; test1(unsigned char i) { switch(i) { case 1: j = 1; break; case 2: j = 2; break; 10 case 3: 11 j = 3; 12 break; 13 default: 14 break; 15 } 16 } unsigned char j; test2(unsigned char i) { switch(i) { case 1: j = 1; break; case 2: j = 2; break; 10 case 3: 11 j = 3; 12 break; 13 case 4: 14 j = 4; 15 break; 16 default: 17 break; 18 } 19 } With Optimization _test1: ld ld cmp j cmp j cmp j 10 ld 11 ret 12 L9: 13 ld 14 ret 15 L10: 16 ld 17 L6: 18 ret A,(SP+0x3) W,0x0 WA,0x3 t,L10 WA,0x2 t,L9 WA,0x1 f,L6 (_j),0x1 (_j),0x2 (_j),0x3 Figure Switch Table Optimization With Optimization S50000: db db db db _test2: ld ld dec 10 cmp 11 j 12 ld 13 add 14 ld 15 ld 16 L14: 17 ret A,(SP+0x3) W,0x0 WA WA,0x3 gt,L14 IX,S50000 IX,WA A,(IX) (_j),A Microcontroller Specific Optimization Every microcontroller has a specific instruction set The types of instructions vary according to the particular microcontroller The C compiler can generate more efficient code by using instructions specific to a microcontroller Consider the C program example in Figure The C compiler takes advantage of the bit manipulation instructions provided by the microcontroller to generate efficient code C Language Program unsigned char a; test( ) { a &= ~0x1; a |= 0x4; } With Optimization _test: ld clr set ret IY,_a (IY).0 (IY).2 Figure Microcontroller Specific Optimization PROGRAMMING GUIDELINES FOR EFFICIENT C CODE It is important to choose a good optimizing C compiler for your microcontroller project However, besides relying on the C compiler, you can also lend the C compiler a helping hand in generating efficient code by observing certain programming guidelines This section describes these programming guidelines Use Microcontroller Specific C Language Extensions In order to facilitate the portability of C programs, a general rule of thumb is to use ANSI C constructs as much as possible, especially function prototyping For cases that require tighter code, use C compiler’s language extensions – to access hardware, locate variables in memory, specify interrupt service routines, etc The disadvantage is that using language extensions renders the C program non-portable /* Function Prototype Examples */ void void_ptr_func(); /* function returning nothing */ char *char_ptr_func(); /* function returning a pointer to char */ int ifunc( char B, int *DEW ); /* function returning an int, 1st parameter is a char, 2nd parameter is a pointer to int */ /* Prototype declaration for interrupt processing functions.*/ extern void int5(), inttc1(), intsio2(), int3(), inttc4(),intsio1(), inttc3(),int2(), inttbt(), int1(),inttc1(), int0(), intwdt(), intsw(), reset(); /* Definition of vector table */ #pragma section const interrupt near 0xffe0 /* const or code can be used for section type */ void (*intvector[])() = { int5, inttc1, intsio2,int3, inttc4, intsio1, inttc3, int2, inttbt, int1, inttc1, int0, intwdt, intsw, reset }; /* Define pointer variable (or array) in vector table */ /* Initialize to set the function address (function name) */ Figure 10 Function Prototype Examples static void DecSafeTicks(int nSafeTicks) { DI; /* special function - disable interrupts */ nTicks -= nSafeTicks; asm(" ei"); /* inline assembly - enable interrupts */ } /* An example of I/O variable definition using directive #pragma */ #pragma io port0 0x00 unsigned char port0; /* An example of I/O variable definition using _io */ unsigned char io(0x00) port0; Figure 11 C Language Extension Examples Use C Compiler’s Optimization Options The C compiler may provide multiple levels of optimization For examples, optimization levels 0, 1, and Usually, the higher the level, the more optimization methods will be used by the C compiler to generate machine code However, depending on the coding style, it is possible that the size of generated code of level is larger than that of level Level Function Minimum optimization (default) Stack release absorption Branch instruction optimization Deletion of unnecessary instructions Basic block optimization Propagation of copying restricted ranges Gathering of common partial expressions in restricted ranges Optimization of more than basic blocks Propagation of copying whole functions Gathering of common partial expressions of whole functions Maximum optimization Loop optimization and other miscellaneous optimization Figure 12 C Compiler's Optimization Options Example The C compiler may have an option that minimizes program size Use it if it is available A possible side effect of this option is that in certain situations, the resultant code may be smaller in terms of number of bytes, but it may execute slower than the code generated without specifying the option Format -XS Function Specifies the output of minimum object code size Description When this option is specified, part of optimization is skipped The default, when this option is not specified, is the output of code with execution speed priority Figure 13 C Compiler Code Size Optimization Example Optimize Usage of Memory Spaces Many microcontrollers have more than one memory space For example, a memory space may be accessible with an 8-bit offset, another memory space requires a 16-bit offset, still some memory space requires an address space modifier You can decrease program size by explicitly locating the frequently used variables into the memory space that requires the minimum number of bytes for addressing Example: int a0 = 0, a1; /* default memory area */ int tiny at0 = 0, tiny at1; /* to tiny area */ void fcn(void) { a1 = a0; at1 = at0; } Opcode _fcn: ; 16-bit address offset E1000048 R ld WA,(_a0) F1000068 R ld (_a1),WA ; 8-bit address offset E00048 R ld WA,(_at0) F00068 R ld (_at1),WA FA ret Figure 14 Optimize Usage of Memory Space Example Use of the const Keyword Using a constant is more efficient than a const variable in terms of execution speed and program size The C compiler can easily access a constant with immediate addressing whereas it needs to use index addressing to access a const variable which is usually placed in ROM However, if a function argument is a pointer to a read-only string (a const data object), using the const keyword to declare the pointer argument may help the C compiler, in certain cases, to generate more efficient code Example: #pragma section const const int ix = 3000; char y[] = { 'A','B' }; static char z = 4; A const declaration can only be used for variables defined in const or code sections, or external variables Variables in const sections automatically take the const attribute and no const declaration therefore needs to be coded, if a const declaration is made for variables in a const section The const declaration also affects external variables Figure 15 Use of const Keyword Example Use Of Auto Variables For temporary variables, not declare them as global variables Rather, declare them as auto variables When global variables are passed as arguments in a function, use the arguments, not the global variables, in expressions within the function For global variables that are accessed frequently in a function, make a copy of the global variables as auto variables and use the auto variables within the function Consider the C program examples in Figure 16 The C program produces 30 bytes of machine code whereas the C program generates 18 bytes of machine code C Language Program C Language Program unsigned char *a, j; test( ) { for (j=0; j[...]... GUIDELINES FOR EFFICIENT C CODE It is important to choose a good optimizing C compiler for your microcontroller project However, besides relying on the C compiler, you can also lend the C compiler a helping hand in generating efficient code by observing certain programming guidelines This section describes these programming guidelines Use Microcontroller Specific C Language Extensions In order to facilitate... the const keyword to declare the pointer argument may help the C compiler, in certain cases, to generate more efficient code Example: #pragma section const const int ix = 3000; char y[] = { 'A','B' }; static char z = 4; A const declaration can only be used for variables defined in const or code sections, or external variables Variables in const sections automatically take the const attribute and no const... WA _SumCount SP,SP+0x4 (_sum),WA Figure 22 Passing Pointer in Function Argument Example CONCLUSION We have described some of the commonly used optimizing methods of C compilers Not all C compilers are created equal Therefore, it is important to choose a C compiler that incorporates good optimizing methods in generating code for your particular microcontroller We have also discussed a set of C programming... output of minimum object code size Description When this option is specified, part of optimization is skipped The default, when this option is not specified, is the output of code with execution speed priority Figure 13 C Compiler Code Size Optimization Example Optimize Usage of Memory Spaces Many microcontrollers have more than one memory space For example, a memory space may be accessible with an 8-bit... within the function For global variables that are accessed frequently in a function, make a copy of the global variables as auto variables and use the auto variables within the function Consider the C program examples in Figure 16 The C program 1 produces 30 bytes of machine code whereas the C program 2 generates 18 bytes of machine code C Language Program 1 C Language Program 2 1 unsigned char *a, j;... portability of C programs, a general rule of thumb is to use ANSI C constructs as much as possible, especially function prototyping For cases that require tighter code, use C compiler’s language extensions – to access hardware, locate variables in memory, specify interrupt service routines, etc The disadvantage is that using language extensions renders the C program non-portable /* Function Prototype... data size that matches the natural width of the microcontroller s registers Also, use the smallest data type that can get the job done For example, if you write a C program for an 8-bit microcontroller, use the unsigned char data type in loop control operations, as subscript of arrays and as bit-field members If the C compiler enforces ANSI C s integer promotion rule by default, specify an option to... Memory Space Example Use of the const Keyword Using a constant is more efficient than a const variable in terms of execution speed and program size The C compiler can easily access a constant with immediate addressing whereas it needs to use index addressing to access a const variable which is usually placed in ROM However, if a function argument is a pointer to a read-only string (a const data object),... ANSI C s rule will enlarge the program size Example: struct field { unsigned char a:1; unsigned char b:3; unsigned char c: 3; unsigned char d:1; }; struct field array[10]; _fcn: ld ld ld add IY,_array IX,IY BC,IY IY,0xa ld ld and or ld or inc inc cmp j ret DE,BC A,(DE) A,0x8f A,0x50 (DE),A (IX),0xe BC IX IX,IY lt,L4 L4: void fcn( ) { unsigned char i; for (i=0; i < 10; i++) { array[i].b = 5; array[i] .c. .. void_ptr_func(); /* function returning nothing */ char *char_ptr_func(); /* function returning a pointer to char */ int ifunc( char B, int *DEW ); /* function returning an int, 1st parameter is a char, 2nd parameter is a pointer to int */ /* Prototype declaration for interrupt processing functions.*/ extern void int5(), inttc1(), intsio2(), int3(), inttc4(),intsio1(), inttc3(),int2(), inttbt(), int1(),inttc1(), ... (_j),A Microcontroller Specific Optimization Every microcontroller has a specific instruction set The types of instructions vary according to the particular microcontroller The C compiler can generate... and recommend C programming guidelines that will result in optimized code for microcontroller applications PROGRAMMING MODEL Some microcontrollers not have hardware support for a C stack If you... embedded applications in C, you should select a microcontroller with a stack-based architecture If the microcontroller has dedicated address-specifying/index registers, they will also help the C compiler

Ngày đăng: 08/03/2016, 11:39

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan