PICC-18 allows RAM objects of any size to be declared, though some limitations exist that require balancing objects between C source files in certain cases. C18 does not support RAM objects larger than 256 bytes by default; creating such objects requires editing linker control files and adding pragmas to the C source which include hard-coded variable addresses. These objects can only be accessed through pointers, not directly.
C18 has 32-bit floats, doubles, and long doubles. PICC-18 has 24-bit floats, and either 24- or 32-bit doubles and long doubles, specified by a command line option.
PICC-18 performs sign-extension when right-shifting signed integers; C18 does not.
A char is unsigned on PICC-18 and signed on C18. The default behavior can be changed with a command line option on both compilers.
A PICC-18 pointer can point to both ROM and RAM objects; the appropriate address space is determined at runtime. C18 pointers can point to ROM or RAM, but not both. Pointers to near are optimized to eight bits on PICC-18; C18 uses a standard 16-bit pointer.
PICC-18 uses a compiled stack for function arguments and local variables. C18 uses a software stack, but an "overlay" model is available with a command line switch.
C18's startup code calls main(), using a hardware stack level; PICC-18 branches to main() without using a hardware stack level.
Upon returning from main(), PICC-18 jumps back to the startup code. C18 calls main() again, without running the startup code to initialize variables again.
C18 supports a 24-bit short long type.
void interrupt isr(void)
{
/* ... */
}
C18 requires the use of a stub function with an assembly language
directive, along with several #pragmas:
void isr(void)
{
/* ... */
}
#pragma code high_vector=0x08
void interrupt_at_high_vector(void)
{
_asm GOTO isr _endasm
}
#pragma code
#pragma interrupt isr
Both compilers support low- and high-priority interrupt handlers, and
both compilers make use of the fast call stack.PICC-18 handles ISR context saving automatically. C18 requires the programmer to be aware of certain conditions that require adding #pragma directives for a correct context save.
Both compilers may place near variables at address zero. A pointer to such a variable will compare equal to a null pointer, violating the ANSI/ISO constraint that a pointer to an object may not compare equal to a null pointer.
__CONFIG(2, BORDIS & PWRTEN & WDTDIS);Any unspecified words are left at their default values. C18 requires all words to be specified; e.g.:
_CONFIG_DECL(_OSCS_OFF_1H & _OSC_HS_1H,
_PWRT_ON_2L & _BOR_OFF_2L, _WDT_OFF_2H,
_CONFIG3H_DEFAULT,
_STVR_OFF_4L & _LVP_OFF_4L & _DEBUG_OFF_4L,
_CONFIG5L_DEFAULT, _CONFIG5H_DEFAULT,
_CONFIG6L_DEFAULT, _CONFIG6H_DEFAULT,
_CONFIG7L_DEFAULT, _CONFIG7H_DEFAULT);
Both compilers support C++/C99-style (//) comments.
Both compilers support inline assembly language.
Both compilers have macros which will insert common machine instructions such as sleep and clrwdt.
C18 supports anonymous structures and unions.
Both compilers support both 16- and 24-bit pointers to ROM. C18 uses the far keyword to denote a 24-bit pointer; PICC-18 requires making all pointers either 16- or 24-bit with command line options.
On processors which have an external memory bus, PICC-18 uses the far keyword to place a variable in the external address space. C18 requires editing linker files and using pragmas to locate varibles in external memory.
Both compilers have header files which define the PIC18 registers and register bits. PICC-18 allows individual bit names to be referenced directly (e.g., RA4); C18 uses a more verbose syntax (e.g., PORTAbits.RA4).
PICC-18 supports a persistent qualifier, which removes a variable from the standard zeroing during C startup. These variables will retain their values across most types of processor reset. Library routines are included to validate the state of persistent variables.
C18 includes an extensive library of functions that interface to the PIC's peripherals and also provide software implementations of peripherals such as a UART.
C18 allows the static qualifier to be applied to function parameters. This causes the parameter to be passed through global memory rather than on the software stack.
C18 has an overlay storage class which can be applied to local variables when operating in non-extended mode. overlay variables can only be used in non-recursive functions, and are statically allocated, much like a compiled stack.
Neither compiler supports bit-fields larger than eight bits.
PICC-18 generates errata NOPs on processors that require them.
picc18 -18f452 file.cwill compile and link file.c and generate file.hex. With C18, each step must be invoked separately; e.g.:
mcc18 -p18f452 file.c mplink file.o c:\mcc18\lkr\18f452.lkr -lc:\mcc18\lib -o file.hexBoth compilers can easily be used with a make utility, and both are integrated into Microchip's MPLAB IDE.
Both compilers can produce list files showing the assembly language generated for each line of C code. PICC-18 generates a symbolic list file for each C source file. C18 generates a single non-symbolic list file for the entire project. PICC-18's list file includes the preprocessed source code, while C18's is not preprocessed. Here is a comparison of listing file formats:
PICC-18:
115 ;t.c: 8: c = a * b; 116 000038 C0FC F002 movff _b,btemp+2 117 00003C C0FD F003 movff _b+1,btemp+3 118 000040 C0FA F000 movff _a,btemp 119 000044 C0FB F001 movff _a+1,btemp+1 120 000048 EC2C F000 call awmul 121 00004C C004 F0FE movff btemp+4,_c 122 000050 C005 F0FF movff btemp+5,_c+1 127 ;t.c: 15: func(10); 128 000056 0E0A movlw 10 129 000058 DFE7 call _funcC18:
0000e4 50e8 MOVF 0xe8,0x0,0x0 c = a * b; 0000e6 036a MULWF 0x6a,0x1 0000e8 cff3 MOVFF 0xff3,0x6e 0000ea f06e 0000ec cff4 MOVFF 0xff4,0x6f 0000ee f06f 0000f0 036b MULWF 0x6b,0x1 0000f2 50f3 MOVF 0xf3,0x0,0x0 0000f4 276f ADDWF 0x6f,0x1,0x1 0000f6 516d MOVF 0x6d,0x0,0x1 0000f8 036a MULWF 0x6a,0x1 0000fa 50f3 MOVF 0xf3,0x0,0x0 0000fc 276f ADDWF 0x6f,0x1,0x1 0000fe 0e0a MOVLW 0xa func(10); 000100 6ee6 MOVWF 0xe6,0x0 000102 dfe3 RCALL 0xcaBoth compilers can produce linker map files. The C18 map file provides information about each code section, total ROM/RAM size, and a list of symbols sorted by both name and address. The PICC-18 map file shows the actual linker command line used, the call graph for the compiled stack, information about sections, information about code size and location in each object file, and a list of symbols sorted by either name or address, but not both. PICC-18 displays information about total ROM/RAM size on the console when linking.
PICC-18 supports both Windows and Linux; C18 supports only Windows.
The target hardware consists of what I would consider a "typical" PIC application, using the A/D converter, UART, MSSP, timer 1 RTC, LCD, and varous switches, buttons, and LEDs.
The software is built on a generic state machine/event scheduler. It contains no floating point, but does include 16-bit integer multiplies and divides. No "hardware library" functions of either compiler are used.
On this particular CPU, enabling extended mode results in there being no access RAM available. Since the code does make use of "near" variables for reduced code size, necessary #ifdefs were included to allow the code to be built under C18 with near variables in standard mode as well as without near variables in extended mode. Results are reported for both compilation methods. PICC-18 does not support extended mode, and would probably not benefit from it, since it does not have a software stack.
The -O+ optimization flag was used for C18; the -O -Zg1 flags were used for PICC-18.
After the initial low-level driver code was developed and integrated into the state machine engine/event scheduler, the code size looked like this:
| MPLAB C18 | Hi-Tech PICC-18 | |
|---|---|---|
| Extended mode without near variables | 4243 bytes | |
| Standard mode with near variables | 4197 bytes | 3246 bytes |
The C18 code was 29% larger than the PICC-18 code.
In order to determine if the overhead of the software stack was responsible for the large disparity in code sizes generated by the two compilers, an effort was made to build the code under C18 with the -sco command line option. This option effectively enables a compiled stack by using static allocation for both function parameters and local variables. Unfortunately, the code would not compile with this option due to its use of function pointers. The user's guide suggested declaring function pointer arguments as auto to get around this problem. Doing so allowed the code to compile, but the link failed with "Error - higher order function calls not supported yet in the presence of the overlay storage class".
The code which used function pointers was then removed, and the -sco build completed successfully. The resulting C18 code size was 3435 bytes vs. PICC-18's 3108 bytes. The code size difference was reduced to 11%, which indicates the price of the software stack is very high.
This code is still in progress; this information will be updated as the code gets larger.
| Compiler | Code size (bytes) |
|---|---|
| MPLAB C18 | 23531 |
| Hi-Tech PICC-18 | 29358 |
The PICC-18 code was 25% larger than the C18 code. Surprised at this significant difference in results relative to the previous comparison, I investigated which C18 optimization was providing such a substantial reduction in code size. Disabling the procedural abstraction optimization changed the results to:
| Compiler | Code size (bytes) |
|---|---|
| MPLAB C18 | 28891 |
| Hi-Tech PICC-18 | 29358 |
The size reduction due to procedural abstraction was 19%. I repeated this test on the commercial application and found the size reduction due to procedural abstraction was only 11%. Further investigation showed significant amounts of repetitious code appeared in the software. Some of the repetition was not obvious: e.g., heavy use of structure pointers which generate a tremendous amount of code from a small amount of C. Procedural abstraction proved very advantageous here.
The TCP/IP stack code contains support routines designed to support pointer limitations in C18 (e.g., XLCDPutROMString) which aren't necessary under PICC-18, but are compiled in anyway. As a result, PICC-18 is at a small disadvantage in code size.
void main(void)
{
double c, s, u, Ta, Tc, Tw;
c = 18.0;
s = 118.0;
u = 1.2/c;
Ta = 60.0/s - 307.2/c;
Tc = Ta/179.0*38.0;
Tw = Ta/197.0*7;
u = .12/c + Tc;
Ta = 6.00/s - 73.2/c;
Tc = Ta/91.0*3.0;
Tw = Ta/190.*7 + Tw;
u = 1.12/c;
Ta = 610.0/s - 3.72/c;
Tc = Ta/19.0*3.10 - Tc;
Tw = Ta/109.0*70 + Tw;
u = 21.2/c;
Tw = u + Ta + Tc;
}
The code was compiled with maximum optimizations on both compilers,
using overlay mode on C18 and 32-bit doubles on PICC-18:
| Compiler | Code size (bytes) |
|---|---|
| MPLAB C18 | 4284 |
| Hi-Tech PICC-18 | 1810 |
The C18 code was 137% larger than the PICC-18 code.