PIC18
Compiler Comparison - CCS, IAR, Hi-Tech and Microchip C18
[many thanks to Trampas
Stern for this excellent compiler comparison article]
Background
I am new to PIC development
and have always heard that the PIC is easy to work with. Thus when I started
designing a board I put four PICs on it. The main controller is a PIC18F8720.
My product is low volume thus I try to place more processing power than needed
on the board to reduce development time by being able to write code in C,
ah the dream of engineers. Thus I needed a C compiler and purchased the CCS
PCH compiler. I got all my low level routines working and when I went to create
a linked list the CCS compiler broke. Thus while waiting for them to fix the
bugs I decided to review the other compiler options. This is not a review
about code size or speed, but more about how easy is it to produce code with
the compiler.
I also have noticed something
during my experiences with the PIC. Everybody wants to create an IDE/editor,
it must be fun or something? That is MPLAB is trying it and every compiler
vendor is trying it. This does not make sense to me as that they never have
a good editor, or a good compiler it seems. I personally find that CodeWright
or UltraEdit beats each and every one of them. The only IDE I will use is
Visual Studio and then only because of the Visual Assist add-on by www.wholetomato.com,
I am not associated with this company; I just love their product! I often
wonder why these compiler companies just don't purchase or use a free editor
from someone and focus their energies on their compiler. In case there is
anyone from these companies reading this check out this link: http://www.ticz.com/homes/users/nlewis/HTML/Software_Development/CodeSense/Documentation/intro.htm.
I also noticed that most
all of the compiler vendors have written their own compilers from scratch.
I often wonder why, when such as GCC exist. I wonder if they could use the
GCC front end and get better error reporting capabilities. I have actually
have loaded my code into Visual Studio and compiled it to determine what one
of the more cryptic errors from a compiler actually meant.
CCS
PCH (v3.136):
The price of this compiler is nice and it is good for hobbyist. Also read
the documentation carefully as that the compiler is case insensitive and an
int is defined as unsigned by default. [Portions of feedback removed due to
adverse reaction from from Hans Wedemeyer - Ed.]
CCS Customer
Support
CCS's tech support is good and responsive with the usual 24 hour email response
time. They also have a user's message forum which is very responsive.
Testing
CCS informed me that they use an internal test suite of code running on emulators
to test their compiler. I also downloaded a copy of 3.139 from their website
which was placed there with out testing and I had to wait for a couple of
hours for them to email me an older version of their compiler which was not
broken.
Libraries
All their libraries for CCS are internal functions. They include a good mix
of functions.
Hi-Tech
PICC-18 (v8.20PL4):
I downloaded the evaluation version and went to compile some code. Right out
of the box it crashed the compiler on the code I had. I found that the problem
was a #pragma from another compiler. I also found that their preprocessor
will continue to process data inside of a #ifdef and #endif that is not defined
and provide syntax errors. This was a problem for me as that in that section
I had some non ANSI standard preprocessor commands for the CCS compiler which
had to be removed before I could compile. HiTech's technical support insists
that this behavior is according to the ANSI standard; however none of the
other ANSI compliant compiler I tested had this problem. Also one thing that
really bothered me about the HiTech's compiler is the lack of documentation.
In their demo download there are no manuals. The only link to a manual that
I could find was from DonTronic's website, I could not find a link to the
manual from HiTech's own website!
I actually was compiling
some code using the HiTech's compiler and got no errors in my source a few
warnings, but no errors, and the linking seemed to take forever. Well I figured
I would see if it ever completed, it never did, not at least until it created
a 32GB COD file and ran out of disk space. I guess I should try turning on
optimizations! Ok I am sure I did something stupid, but it is still funny.
HiTech's Customer
Support
Their main office is in Australia, thus if you are in the US planning on working
at night to get tech support. Their sales department needs some work, after
I broke the CCS compiler and was waiting to get some fixes I called HiTech's
US office on Friday afternoon ready to purchase their upgrade product. However
I found that they could not help me and I would have to contact their Australia
office on Monday. This was fortunate for me as I kept having problems with
their compiler over the weekend resulting in me not purchasing their product.
HiTech has also set up a user message forum but it does not appear to be as
active as the CCS one.
Testing
HiTech informed me that they use their own internal generated test code to
test their compiler.
Libraries
The HiTech compiler includes a full suite of libraries
Microchip's
C18 (v2.10)
This appears to be Microchips first compiler and it kind of shows. They implemented
their compiler a bit different than most compilers, in that they have separate
pointers for program and data memory. This leads to a problem as shown below:
char data[]="hello
world"; //data is in data memory and is initialized at program start
up
printf("hello world"); //this string is stored in program memory
only
printf(data);
This code will not work
on C18 as that the first printf is passed a pointer to program memory and
the second printf is passed a pointer to data memory. Thus most all string
processing functions require two versions one where the string is in program
memory and one where the string is in data memory.
I also crashed the C18 compiler right out of the box by not ending my source
code with a newline. I have also currently have crashed the C18 again, which
I am waiting on a response from tech support as to why. For those of you planning
to use v2.10 there are some bugs that no one tells you about, and they do
know it. For example the stdarg.h is incorrect and you can work for hours
like me trying to figure out why your printf function does not work, seems
like they would release a 2.11 to fix it and save us time. I also found that
their conversion utility from cof to cod has a limitation such that you can
not have files in paths over 62 chars in length, I was informed that this
would be fixed in future versions, but that does not help with your problems
today.
Technical
Support
Microchip technical support is good, once you find the right people to email
your questions and comments to. Do not expect much help from their phone support
as they are generic for all of Microchip's products. Like all the others Microchip
has a message board, which is fairly active but it also has a bad user interface,
shows you old messages at the top and forcing you to scroll to the newer ones.
Testing
Microchip informed me that their testing was done with Plum Hall's test suite.
Libraries
You will hate that they do not include a printf statement, but I imagine they
could not figure out how to get around the example above. I did finally figure
out that you could using a custom linker script force all the rom constant
data to be located in memory at a location higher than the end of data memory.
Thus by checking the address of a pointer you could determine if it was a
pointer to data in program or data memory.
IAR's
PIC18 (v2.11A/W32)
I have been using the IAR compiler for a few days now and have found no problems
with the compiler. The demo came with full documentation and a simple IDE
to get started, see my comments about IDEs above, this one is no different.
However this IDE does have a nice feature in that when you get any errors
in a file it will open the file and place markers at each error, thus you
do not have keep click on errors to go to the next error you can just scroll
to the next one. Of course double clicking on the errors will still jump to
the error.
Technical
Support:
Actually I do not know about the technical support, I have not had to contact
them yet. I did find a link to a message board, but have not investigated
it yet.
Testing
IAR informed me that they use the Plum Hall test suite and they also use internal
and customer code, with their permission. They informed me that their customers
have weird code that even the Plum Hall can not always test for.
Libraries
IAR comes with a complete suite of libraries.
Warnings
and Errors
Here what I wanted to
do was provide some sample code of common mistakes programmers make in C and
see how the compilers handle these problems:
//this
is some of the most common mistakes users do in C.
#define set_zero(x) x=0
void main()
{
int a,b,c;
a=3;
set_zero(c) //no semicolon at end
of macro
set_zero(b);
if(b=a) //assignment operator
instead of equality
{
b++;
}
if (b>0 & a<0) //using bit-wise and
not logical
{
//do something
}
if (b>0 | a<0) //using bit-wise or not logical
{
//do something
}
if (b<<3+2) //bit shift has low priorty thus this is b<<5
{ //user most likely wanted
(b<<3)+2
//do something
}
a=b || c; //using logical operator
instead of bit wise
a=b && c;
a==c; //equality instead of
assignment
set_zero(a)=c; //just plain stupid user
switch(a)
{
case 0:
//do something
break;
case 1:
//do something else
break;
case 0:
//Redefinition of case 0
//do something
break;
}
//* What type of comment am I
/* A really dumb comment
//*/
/* A comment where the ending comment
is not found * /
}
//no new line
IAR
compiler
The IAR compiler found the missing semi-colon and reported the error as such
instead of generic syntax error. IAR also found the use of the equality operator,
instead of equals. It got the stupid user error. It got the duplicate case,
the nested comments, and the non ending comment. All in one compile!
Total passes=1
File Code size = 264
ROM after linking =316
RAM after linking = 314
PIC18
Crashed would not begin to compile.
Hi-Tech
C18
HiTech complained about having a EOF file in a comment. Then we had the no
semicolon and the stupid user error. Then we got the error for duplicate case.
Total passes =3
ROM=270
RAM=10
CCS
C v3.412
CCS only reports one error at a time.
No semicolon error.
No Lvalue, aka stupid user error.
Duplicate case error.
No ending comment error.
Total passes = 5.
CCS reports percentage
of chip use plus the bytes used. [Review updated - Ed.]
Visual
Studio 6.0
I use Visual Studio as the golden standard for errors and warnings. The only
warning that MSVC++ found that IAR did not was the use of bit wise operators
in the if statements. MSVC++ did not care about nested comments.
Things
I Have Learned
First and foremost a C compiler will not abstract the processor like you get
with MSVC++. If you do not know the datasheet of the processor by heart you
will before your project is done. Using any of the compilers will at some
point require you to read and understand the assembly, thus just accept it
and learn the assembly. You will have to know what each special purpose register
and each bit does in the processor. Therefore as you read stuff below it may
sound hard to the first time user but it will save you hours latter on.
Thoughts
on programming
If you are going to be doing a lot of programming in the future a wise investment
would be to have a set of tools always in your back pocket. Specifically I
would recommend knowing at least one of the following:
1) make - For compiling and linking your code
2) A good editor - Most IDEs suck! Learn a good editor of your choice
3) Scripting language - You will eventually need to make a script learn a
good language for this like perl, python or TCL.
4) Macro language - Every assembler includes a macro language, learn one powerful
one like M4 and use it for all them. You need to have a macro language that
supports variable length arguments and argument inspection. This will save
you days of time.
5) Version Control - enough said.
To give you and example
of how these thing can help you, I use CodeWright as my editor were I create
my projects, also links to version control. Then I run a script which converts
the CodeWright project to a make file and checks dependencies. Then when I
hit build in CodeWright it runs the make file, reports errors and jumps to
the errors in the source file. Plus this process works for all compilers and
languages!
When I do assembly level
programming I like to make a macros called function, fcall, fret, and fresult
which are used like:
function(my_add, a,b) //macro pops a and b off stack and store in appropriate
registers
//add the a and b and save in a
fret a //return result put result in return register and pop PC
//I call the function
like
fcall(my_add, 1, 2) //my macro figures out type of parameters and push on
stack
//then it calls the function setting the return address
fcall(my_add, r0,r1) //ro and r1 are registers and the macro figures this
out..
There is more to this than I show and it depends on the assembly language,
but you get the idea. This is not always the fastest code but it reduces the
stack errors significantly.
Data
Types and Sizes
ANSI compliance is worthless. Each and every compiler defines things like
the ports and the bits of the ports differently. Thus your code will never
be portable unless you plan ahead. To plan ahead, start by defining your data
types. For example I never every use an int, why because on some compilers
it is a byte, some an unsigned byte and some even say it is a 16-bit value.
I always defined my own types similar to this:
typedef signed char BYTE;
typedef signed short WORD;
typedef signed long DWORD;
typedef float FLOAT;
typedef unsigned char UBYTE;
typedef unsigned short UWORD;
typedef unsigned long UDWORD;
Do not use any bit data
type if you want your code to be portable!
Ports
and Registers
Decided how you will use them and keep to that method. For example I try to
do the following: PORTA is port A, TRISA is tris A. This is the way most,
but not all compilers operate. Specifically CCS is different. Define each
bit in the port for example bit 0 of port A would be: RA0 in my world, this
is the way HiTech and IAR work. The C18 and CCS compilers used to be different,
but CCS has introduced a similar feature in the later versions.
In the HiTech, C18 and
IAR you can do the following:
RA0=1;
For the C18 you will have to define RA0 like:
#define RA0 PORTAbits.RH0
However there is no way
to do such an assignment in the CCS compiler their you have to call a internal
function:
#define RA0 PIN_A0
output_high(RA0);
The net result is that
if you want to keep your code portable to the CCS compiler you would need
to do something like this for all the other compilers:
#define output_high(x) {x=1;}
Thus you would have to use the output_high function/macro anytime you set
a bit.
[CCS have introduced a
function in later versions which makes this CCS port assignment unnecessary
- Ed.]
Intrinsic
Functions
Just about every compiler has them and if you want portable code you should
try and not use them. Instead create your own. For example every compiler
seems to have an intrinsic function for enabling global interrupts and resetting
the watch dog. Instead write your own macros that does these functions:
#define GlobalInterupts(x) {GIE=x;}
#define Nop() {asm("nop");}
#define ClrWdt() {asm("clrwdt");}
#define Sleep() {asm("sleep");}
#define Reset() {asm("reset");}
Data
type conversions
Write your own data type conversion functions. For example here are some of
mine, I will not guarantee they are correct!
#define GET8(x,n) ((x>>(n*8) & 0xFF))
#define MAKE16(x,y) (((((WORD)x)<<8)) | ((WORD)y))
#define BIT_SET(x,n) (x=x | (0x01<<n))
#define BIT_TEST(x,n) (x & (0x01<<n))
#define BIT_CLEAR(x,n) (x=x & ~(0x01<<n))
#if LITTLE_ENDIAN
#define MAKE_WORD(x,y) MAKE16(y,x)
#else
#define MAKE_WORD(x,y) MAKE16(x,y)
#endif
Notice the last one. This
will save you hours latter on when you swap compilers that use different endianess!
Also some compilers offer faster intrinsic functions for doing some of these
functions, if so all you have to do is redefine them in one spot.
Printf's
You would not believe the problems printf will give you in porting your code.
The best thing you can do is be careful and possible use a macro. An example
will help:
char data=3;
printf("Hello %d",data);
This will work on most compilers but some say the default for %d is a 16 bit
value and that you would need a %hd for 8 bit. Then some say that you need
%ld for 16 bit while others say this is only for 32 bit values. This is a
big headache with no easy solution from my chair, maybe someone else knows
a solution besides rewriting the function, which I know a lot of you have
done. Pay particular attention to printfs if you are considering porting to
the C18.
[Thanks to Trampas Stern for this excellent article - Ed.]
Feedback
2008-04-08-1303 - Received email:
Shane Tolmie
I'd like to comment on the article "PIC18 Compiler Comparison - CCS, IAR, Hi-Tech and Microchip C18" located here:
http://www.microchipc.com/reviews/PIC18_compiler_comparison/
in response to:
"
However there is no way to do such an assignment in the CCS compiler their you have to call a internal function:
#define RA0 PIN_A0
output_high(RA0);
The net result is that if you want to keep your code portable to the CCS compiler you would need to do something like this for all the other compilers:
#define output_high(x) {x=1;}
Thus you would have to use the output_high function/macro anytime you set a bit.
[CCS have introduced a function in later versions which makes this CCS port assignment unnecessary - Ed.]
"
I have used the following method (example code provided) with success, removing the need to call the built in port manipulation functions in CCS:
"
#byte PORTC = 0x07 //PORTC is at SFR address 0x07
#define LED1 0x80 //LED1 on PORTC MSB
void main()
{
while(1)
{
PORTC|=LED1; //Turn off LED1
PORTC&=~LED1; //Turn off LED1
PORTC^=LED1; //Toggle LED1
}
}
"
I'm not sure how this method would port to other compilers, but this might help someone else.
Thanks,
Aaron Carlton
2008-07-07-1017 - Received email:
In PIC18 Compiler Comparison - CCS, IAR, Hi-Tech and Microchip C18
In the HiTech, C18 and IAR you can do the following:
RA0=1;
For the C18 you will have to define RA0 like:
#define RA0 PORTAbits.RH0
However there is no way to do such an assignment in the CCS compiler their you have to call a internal...
This is wrong and has been since the start. It is much the same as C18.
Your note
CCS have introduced a function in later versions which makes this CCS port assignment unnecessary - Ed.]
is also wrong.
Peter Anderson has been doing this for years.
CCD Code:
#byte PORTA = 0xF80
// ------- PORTA Bits -----------------------------------
#bit RA6 = PORTA.6
#bit RA5 = PORTA.5
#bit RA4 = PORTA.4
#bit RA3 = PORTA.3
#bit RA2 = PORTA.2
#bit RA1 = PORTA.1
#bit RA0 = PORTA.0
__________________
Daniel Johnson
More
Reviews
Review
on CCS C for PIC18x.
Review on Hi-Tech for PIC18x
Review on Hi-Tech for PIC12x, 16x and
17x.
|