Search this site (

CCS C Compiler for Microchip PIC micros

Your ad here

  PIC18 Compiler Comparison - CCS, IAR, Hi-Tech and Microchip C18

[many thanks to Trampas Stern for this excellent compiler comparison article]


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, 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:

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.


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.


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.


HiTech informed me that they use their own internal generated test code to test their compiler.


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

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.


Microchip informed me that their testing was done with Plum Hall's test suite.


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.


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.


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;

      set_zero(c) //no semicolon at end of macro
      if(b=a) //assignment operator instead of equality
      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

            case 0:
                  //do something
            case 1:
                  //do something else
            case 0:                //Redefinition of case 0
                  //do something

      //* 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


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

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:

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

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))

#define MAKE_WORD(x,y) MAKE16(y,x)
#define MAKE_WORD(x,y) MAKE16(x,y)

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.


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.]


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:

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

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()
        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.


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:

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.

This site is non-profit. Ad revenue almost covers hosting costs.

We welcome any suggesions or comments! Send them to Shane Tolmie on This site is a completely separate site to, and is maintained independently of Microchip Ltd., manufacturers of the PIC micro. All code on this site is free for non-commercial use, unless stated otherwise. Commercial use normally free, however, it is prohibited without contacting for permission. All content on this site created by Shane Tolmie is copyrighted by Shane Tolmie 1999-2009. Click to advertise on this website - $29.90 for a banner ad which will reach 55,000 user sessions per month. One months free trial!