|
|
View previous topic :: View next topic |
Author |
Message |
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
The quirks and flaws of the CCS compiler |
Posted: Tue Apr 16, 2013 1:32 pm |
|
|
The list or quirks and flaws for the CCS compiler is rather extensive. I was recently asked to write some code for a PIC18, and decided to adopt the CCS compiler to save some time manually setting up registers for every peripheral (I was naively thinking it would be vaguely similar to Arduino). Unfortunately, learning all the quirks and flaws of this compiler has ended up wasting probably far more of my time than would have been saved by whatever level of abstraction the CCS compiler provides. Anyway, the level of abstraction provided by the built in functions is minimal, and the documentation can be quite sparse in some cases, leaving you guessing what the built-in function is actually doing. In any case, in the end it is a capable compiler if you are aware of all the flaws and quirks, and understand that it is not compliant with any C standard (consider it to be a unique C variant, "CCS C"). So I’ve compiled a list of all the flaws and quirks of CCS C and the CCS compiler (specifically PCH v4.140), for easy reference. There may be more quirks that I’ve yet to encounter. I also recommend some settings that work best for me personally.
1. The compiler is not case sensitive by default. Use #case before your code to fix this.
2. This is a single-pass compiler, meaning that in the MPLAB X project, you can only have one source file: main.c. Within main.c, #include all of your source (.c) files.
3. All built-in data types (e.g. int16) are unsigned by default (the basic data types are signed by default in every other C compiler I’ve ever used).
4. Right shifts (>>) of signed integers does not shift in the sign (so 0xF1>>4 becomes 0x0F instead of 0xFF). This may be standard behavior, I don’t know; just noting it.
5. The compiler ignores “#fuses WDT” if “#device ICD=TRUE” exists. The compiler does not issue an error or warning, it just secretly ignores your fuse.
6. The compiler doesn’t do any type checking on pointer types. This flaw is the worst of them all! You can assign pretty much anything to a pointer variable without any errors or warnings (including non-addresses, numbers, and addresses to functions). You can also specify function arguments as pointers to undefined types in the function definition, and the compiler will not complain. Ridiculous but true. The theory is that the compiler is not ANSI compliant, but compliant with the K&R standard which is 24 years obsolete. Basically, don’t count on the compiler to catch type-mismatch mistakes that a “real” C compiler would.
7. There is a CCS compiler bug in ver 4.140 (and earlier?): when you use -> to access a bool (i.e. uint1) in a struct, the bool will not evaluate correctly except with boolean operators (e.g. using “!=” won't evaluate correctly). Also, printf() will not print out the correct value. Explicit casting to int8 doesn't help. Note that this is a problem when using the ‘->’ operator, but doesn’t appear to be a problem when using ‘.’. SO, always use bit-fields in structs instead of bool, or copy out the value to a local bool before using it.
8. CCS claims that function overloading is supported. This does not seem to be the case except for limited argument types, and is certainly not the case for pointer arguments (see #6 above). Basically, it is generally best to avoid using function overloading. Not a big deal, but annoying since it is an advertised falsehood (mostly).
9. In CCS, the “const” keyword has been re-purposed to mean “stored in program memory” (unless using #device CONST=READ_ONLY). This can be VERY confusing if you don’t keep it in your head that const does not mean what it normally means. Additionally, there is the poorly documented ‘rom’ keyword that does basically the same thing as const. SO: use #device CONST=READ_ONLY, and use the rom keyword to put const data in program memory.
10. Be sure to only use rom pointers when accessing data that was declared rom. If you put a pointer to rom data in a normal (ram) pointer, that pointer will look for that address in ram, not rom. This is reasonable; the main issue is that there is no pointer type checking, so you will not get an error if you screw this up (back to problem #6 above again!). Screwing this up can be easy to do when passing a pointer to a function. If the function argument is declared foo(uint32* ptr), then any pointer passed in will be assumed to be pointing to an address in RAM. Likewise, foo(rom uint32* ptr) will treat ptr as an address in ROM, regardless of the type of pointer that you pass in (ram or rom). What this means is that functions with pointer arguments can only be passed addresses from RAM OR ROM, you can’t mix and match. HOWEVER, the built-in functions WILL work with either RAM or ROM pointers.
11. String literals are always stored in ROM regardless of any #device directives (I think). This means that string literals cannot be passed directly to user-defined functions with char* arguments (but functions that are built into the compiler do not have this limitation). You can get around this problem by using #device PASS_STRINGS=IN_RAM which instructs the compiler to copy the string from ROM to RAM in these situations so that a valid RAM pointer can be passed. Obviously copying strings to RAM is time-consuming. Another way to get around it is by using the goofy special case in CCS where a function declared as foo(char ch) will be called repeatedly if passed a string instead of a char, one call for each char in the string; however, this only works with one-argument functions, and is yet another non-standard quirk that will confuse people who think they’re reading C code. SO: use #device PASS_STRINGS=IN_RAM. Interestingly, the built-in functions (e.g. printf() and the like) seem to be able to take string literals, pointers to ROM address, and pointers to RAM addresses, without hesitation. This leads me to believe that this directive has no effect except on strings passed to user-defined functions.
12. The first argument of printf() must be a string literal. So storing a string like “%u apples and %u oranges\r\n” into a variable and passing that variable to printf() will print the string exactly as-is and will not fill in the %u’s, nor will it interpret the \r\n. It will print it though, regardless of whether the string is located in ROM or RAM. My understanding is that this is because the compiler generates unique code for each call to printf(), so it is not treated as a single function. This is done to increase execution speed.
13. Be careful that enable_interrupts() is not called from within an isr (check the entire call tree). The CCS compiler does not support interrupts while in an isr and automatically disables them (but that doesn’t mean you can’t re-enable them, subsequently causing RAM corruption).
14. The compiler does now support pointers to functions. This can be very useful. There are still a couple of quirks though. First, the function address cannot be assigned to a pointer except at run-time. Second, the function address must be assigned to a void* pointer (in your struct or whatever), NOT to a “void (*Func)(int8 arg)” pointer. That is, until you go to call the function… then you assign it to the specific function pointer “void (*Func)(int8 arg)” and call (*Func)(0).
15. As with many of the built-in CCS functions, the details of what they do aren’t clearly explained in the manual. Calling read_i2c() will release the clock. If you aren’t immediately exiting the ISR, then you must not call this function or the master could send another byte while you’re still in the ISR. Read the data register directly, then call read_i2c() just before exiting the ISR (this is how to do clock stretching). Also, the documentation for read_i2c() says that it acknowledges by default; however, it seems that the opposite is true – always specifically specify the ack argument in read_i2c().
Last edited by DireSpume on Tue Apr 16, 2013 5:30 pm; edited 1 time in total |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9225 Location: Greensville,Ontario
|
|
Posted: Tue Apr 16, 2013 2:25 pm |
|
|
As a user of CCS C PCM since v2.359(that's 15+ years) I am upset at your bashing of the product.Your 'tone' is rather condesending but I will offer some comments.
If you really,really hate the product, please, go write your own compiler don't waste your time bashing it.Take the bull by the horns,IF, you are sucessful and make a true ANSI 'C' compiler,that offers as many features as PCM(examples,functions,etc),priced the same and offer similar support then you'll be a happy millionaire.
As for the 'quirks and flaws'..
1) this is a user option,eaily modified by the user , for the user's preference.
2) I don't use MPLAB X so I can't comment
3) again another USER defined option.Who is to say WHICH option should be the default?
4) maybe a specific compiler version 'bug', others might know
5) I never use ICD(never needed it), though I understand WHY the WDT would be disabled.
6) Ok, pointers aren't well done.Write a 'patch' and submit it!
7) This again may be specific to a version bug...
8) I'm selftaught in CCS C, so not sure what 'overloading' is.I'd have to 'google' it,though my code works fine...
9)CONST sounds right to me..the data is 'constantly'(permanently) in memory.It's based upon the architechure of the PIC,and remember CCS C is not ANSI C.Never was,never will be.
10) The programmer should know where the data is.It's unreasonable to think the compiler knows what's you're thinking....
11)Again this is NOT an ANSI C compiler but the manual does explain how to achieve what you want.
12) I'd have to see a real example to comment on this one
13) This goes to PIC hardware and not the compiler.You are free to write your own ISR manager,CCS does allow this option.
14) Again, feel free to write your own pointer functions.
15) The manual could easily be 15-20 times bigger if a 'detailed' description of every function was included.That really goes beyond the scope of a normal manual and 99% of the programmers don't need those details. You can of course, printout the function code and see what is happening.
Overall CCS has provided a great product at a reasonable cost.It has several versions of the compiler that allow code to be quickly written for 1,000s of PIC variants enabling the basic hobbiest to the professional coder to get any PIC project 'up and running'.
I'm sure others here may want to comment and I'm the first to say I'm not an expert, however CCS C does work for me.I've always been impressed with the staff at CCS for the HUGE range of features,functions and PICs they can accomadate! It allowed me to retire at 45 so the code I create now is for fun projects.
jay |
|
|
languer
Joined: 09 Jan 2004 Posts: 144 Location: USA
|
|
Posted: Tue Apr 16, 2013 3:45 pm |
|
|
Quote: | There is a CCS compiler bug in ver 1.140 |
This right there smells funny. You surely are not complaining about a bug in a version many years ago.
Many of your issues have been addressed by compiler directives and or newer versions. And yes there are still limitations; but these are in many ways not different that the limitations on Hitech, CC5X, MikroC or others. You have to remember that this is a compiler for microcontrollers - not microprocessors; and 8-bit at that. And this is a C compiler, not C++.
I am surprised you are surprised you encountered issues re-using the code to the Arduino. In our company we write exclusively in C++ code for many different embedded platforms. Funy thing is that at the beginning of every project we can't seem to emphasize enough how much code will be re-used. Here is what I've seen happen time and time again. Unless we are re-using the code within the same target processor (same TI family, or PowerPC family, etc) the actual code re-use is minimal. Algorithms and state machines are re-used for sure, but to actually copy and paste the main code with minimal changes - yet to see it happen. [EDIT] Yet re-using the code within CCS is a breeze most of the times.
Last edited by languer on Tue Apr 16, 2013 4:10 pm; edited 1 time in total |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1907
|
|
Posted: Tue Apr 16, 2013 3:55 pm |
|
|
I've worked with ~8 compilers in the past 20ish years and CCS is my favourite when it comes to embedded processors.
Does it have quirks? Yes. But in my experience, no more and no less than other compilers.
Just a quick note regarding your point 3. On the 8 bit processors, data types are indeed unsigned by default. However on the 16 bit processors the compiler treats the types as signed by default. That one bit me in the arse recently. |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Tue Apr 16, 2013 4:43 pm |
|
|
My primary purpose in posting that list was as a reference in the hope that a newcomer (such as myself two months ago) might be helped by not having to go through the pain of learning the quirks of the compiler that caused me frustration.
That being said, re-reading my OP, I see that I was pretty critical in my introductory statement (not entirely intentionally). It's true that I am frustrated with some aspects of this compiler (particularly the lack of pointer typing, item #6). That is because I started using this compiler under the foolish assumption that it was an ANSI C compiler. Nowhere in the sales material does it specify that it is not (but it doesn't really say that it IS either). BUT there are things I like about the compiler. And now that I am familiar with all the quirks, it's not so bad. I just found learning all those quirks to be quite frustrating, and I was hoping to save someone else (who may also be assuming it's an ANSI C compiler) that same pain.
In fact, the only items in my list that I think are true flaws and deserving of criticism are #5, 6, 7, and 9 (and again, #6 is the worst; also #8 and 10 would be fixed if #6 was fixed). The others items are just the way it works, but you need to be aware of those issues or they will trip you up (as a new comer).
Also, on #7, I meant v4.140, not 1.140 (I've corrected it). And I never said I reused code from an Arduino project, I was simply stating that my hope with CCS (over the Microchip compiler) was that it would provide a layer of abstraction that would limit the amount of hardware bit twiddling and data sheet translation that I would have to do. And it does to some extent, just not as much as I'd hoped (and my expectations were likely unrealistic given the number of PICs that are supported).
Again, I was intending this post to be helpful. I didn't intend to be quite so critical. However, for the most part I do stand by my criticism. But I also think that overall it is a decent compiler. My main frustration, in fact, is that someone hasn't posted a list like this before! |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Apr 16, 2013 5:05 pm |
|
|
yep, CCS is the worst compiler there is ,
EXCEPT FOR ALL THE OTHERS !!!
BTW: i never have seen your #12 and i do explicitly use "%Lu\r\n" with
no problem.
most of the other problems you mention, have never manifested as issues for my work at all. Of course I can live with the sometimes odd rules of CCS, because it is so much simpler to code with than another compiler offering that did not serve me well.
Anyway, Best of luck with your work.
|
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Tue Apr 16, 2013 5:24 pm |
|
|
Example code for #12:
Code: |
char Str[20] = "%u apples.\r\n";
char apples = 3;
printf(Str, apples);
|
This will print "%u apples.\r\n", NOT "3 apples." with crlf. Unless something has changed since I tried it last. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Apr 16, 2013 5:45 pm |
|
|
Code: |
char Str[20] = "%u apples.\r\n";
|
will try this tomorrow
but w/o a
\0 ,
i bet it prints even more than that .....
though quoting from the user doc:
Quote: |
printf( ) fprintf( )
Syntax:
printf (string)
or
printf (cstring, values...)
or
printf (fname, cstring, values...)
fprintf (stream, cstring, values...) |
you may note that format specifiers are only supported in
Constant strings, and that var strings do not allow a values field to be added to the function call. so this is asking for something they do not support .
i can live with that and it has never caused me any pain or $$ loss.
i for one have always wished the compiler would do auto-casting as i imagine it in my mind, but that has not happened either.
SO....
in general i console my self with the low cost and ease of use i have come to like so much about CCS.
i would need some reason FAR better than any i see in this post to impact my loyalty to the product. |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Tue Apr 16, 2013 6:47 pm |
|
|
The '\0' is there, believe me; you don't have to add '\0' explicitly in string literals; it happens automatically. Anyway, the printf() "quirk" is no problem at all. It is perfectly reasonable that it works the way it does, and I don't have any problem with it. As I've said, this is simply a list of things that might trip up a new user, and I have no problem with most of them (except for the few that I've stated above). |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Wed Apr 17, 2013 12:13 pm |
|
|
I think the first thing is to start from k&R, not later versions of C.
K&R, is explicit that handling of rotating signed is processor specific, that integer size is processor specific, etc. etc..
90% of the 'differences' listed are acceptable in K&R.
The printf format specification, is listed in the compiler manual as a limitation, and is 'inherent' if you understand the limitations of what the compiler is trying to do. It doesn't have a printf function!. It has a library of code, from which it only loads the preselected parts.
Other key thing is to study the chip architecture, and understand that the PIC is a chip with separate ROM and RAM memory spaces. This is the point about const (which was used by CCS _before_ ANSI...). You could say it was ANSI that 're-purposed' it...
You can perfectly well enable ISR's inside ISR's. The only thing you can't do is enable _GLOBAL_ inside an ISR. This is a hardware limitation, and is a case of reading the PIC's application notes.
CCS is not a generic C. It is a C specifically designed to be very close to K&R, and allow you to easily write efficient code in the PIC, but with the limitations of this processor family. |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Thu Apr 18, 2013 2:34 am |
|
|
Well there are coders and electric circuit designers. A designer sees code as an embedded micro circuit. It is the electrons that are paramount. A coder like a language professor sees syntax,grammar and spelling. The language has to have an almost gospel like quality and the coder has the role of a priest in shooting down heresy. The pic for many is a robust electrical circuit. The goal for all is to have the electrons go in and out of the pins as dictated. What is important is the efficiency of the machine instructions in achieving that goal. Now assembler is the king in coding efficient machine instructions. However tasks can be complex and assembler can be so close to the hardware that a complex task becomes verbose. C now becomes attractive to a designer. But the goal of efficient code can't be sacrificed so heresies are accepted. CCS is very efficient but the righteous will overlook that and focus on the heresies. Coders can become disembodied from the electrons that serve them often they have little regard and the electrons are sent on unnecessary journeys to preserve coding purity. If you respect electrons you probably will like the CCS compiler |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Thu Apr 18, 2013 10:47 am |
|
|
You philosophical argument is a good one and there may be some insight there as to why I am so annoyed with certain aspects of this compiler. I did start out as a software engineer coding windows software, and my degree is CS, and I had to write a complete compiler in college, so I have pretty specific ideas on how compilers should behave, inside and out. And since I've been programming in ANSI standard C (and C++) for 20 years, I have developed an expectation for C compilers to behave a certain way. Now as I've repeated multiple times, I don't have a problem with most of the things on my list. However, the fact that the compiler does not do pointer type enforcement (#6) is inexcusable. Enforcing pointer types would in no way affect the final assembly code output, so there is no excuse for the deviation. Well, I know the excuse: the compiler was originally based on the 25-year-old K&R C standard, and CCS is apparently too concerned about backward compatibility with existing code to fix it now. But with as long as CCS has been around, my opinion is that the problem should have been fixed by now. It would not be a terribly difficult thing to add, so I believe CCS has made a conscious choice to preserve backward compatibility at the expense of compile-time error catching, a decision I strongly disagree with. It is incredibly helpful to have full type enforcement, and it changes nothing as far as assembly code speed or size.
Again, my position is simple: if there are any newcomers to CCS that have a background similar to mine, I am certain this list will be helpful to them. And if CCS adds pointer type checking, the compiler will be much improved without anything lost. That's it. |
|
|
Darren Rook
Joined: 06 Sep 2003 Posts: 287 Location: Milwaukee, WI
|
|
Posted: Sat Apr 20, 2013 9:11 am |
|
|
The compiler does have a linker, so you don't need to do everything in a single pass. See the MCU.ZIP in the Examples folder. _________________ I came, I saw, I compiled. |
|
|
dmderev
Joined: 29 Jun 2018 Posts: 2
|
Troublesome compiler experience -anyone has same experience? |
Posted: Fri Jun 29, 2018 11:54 pm |
|
|
I downloaded the latest MPLAB X 4.2 and in the list of plugins it invited to download CCS. I am lazy reading manuals and I was sold on the advertisement that one can now focus on the code, rather than on configurations. And I did not know the history of this compiler.
At the first glance it looked pretty advanced and user friendly. But then I spent painful 4 days to make something practical rather the examples which did work, and I really started to hate it and my discoveries. I needed a hardware interrupt driven UART I/O on PIC18F2455 and used the configuration that, by manual, should provide the instantiation and correct configuration of hardware.
To my surprise, the program produced different and non-standard baud rates and did not respond to baud rate generator settings. Now, I wanted to bypass it whatsoever - but I learned that the compiler is a black box, with no clear access to configuration memory settings, no way to replace the built in routines and very cryptic help file that does not match the actual implementation. For instance, I discovered the undocumented
#use rs232 (call_putc=hwuart_putc, call_getc=hwuart_getc, call_kbhit=hwuart_kbhit)
which I had to use to access my hardware UART driver. It was not sufficient just to have linker link you putc() and getc(). The format print don't work, type casting is missing...
Once done with workarounds, I discovered that the baud rate is still incorrect because the
#use delay(clock=48000000 ,crystal=12000000, USB_FULL )
does not explicitly tell how the internal oscillator divider is configured, and it is configured un-intuitively. And it is difficult to debug these situations!
I also find that the fact that the libraries are invisible and opaque is very frustrating because no way to look why your program does not work as intended.
As the result, the learning curve is not fast and the built-in functions which are designed to help programmer in fact are a curse...
I did not manage the IDE to work with PICKit3. So I used MPLAB IDE with CCS compiler.
Unfortunately, the code generated by MPLAB is absolutely incompatible with CCS, so overriding the creativity of internal compilers' working is impossible.
Now I will get back to Microchip's compiler, and it requires rewriting a program... What a waste of time on evaluation of this product...
Anyone had the similar experience?
Last edited by dmderev on Sat Jun 30, 2018 12:59 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Sat Jun 30, 2018 12:54 am |
|
|
Your 'different' baud rates, were almost certainly simply that the chip was not running at the clock rate you were telling it you were. This is why one of the critical 'first things' we often say here is to do the basic 'flash an LED' test, to verify you have the clock programmed right. The compiler for _all_ timings, is relying on you correctly telling it what clock rate it is running at. It can't guess.
There are very easy ways to 'replace the built in routines'. One of the powers of the compiler is the syntax:
printf(my_putc, "what you want to print");
This will send what you specify to print to the function 'my_putc'. You can make your own I/O routines very easily. Just because it offers you a shortcut way of generating the I/O routines, does not mean you are stuck with them. If you don't specify a #USE RS232, no 'built in routines' for the UART are generated.
One of the big powers of CCS, is it allows all the basic work to be done with a few lines. If you don't want to use these shortcuts, then it'll just let you do things yourself. You can't get much more flexible that that.
On your UART, you made it hard on yourself:
#use rs232 (UART1, baud=9600)
Is all you need to access the hardware UART (at 9600bps). The 'undocumented' routines you used, are designed for a programmer who wants to manually 'separate' the putc from getc, possibly replacing one with their own code. So you are using a tool designed to allow exactly the replacement of the internal routines you say is not available, to use the internal routines. Duh....
You can always simply configure the clock yourself.
Just set the fuses for the clock divider you are using, and then set the clock rate only.
Code: |
#include <18F4550.h>
//for a 20MHz crystal
#fuses PLL5,CPUDIV1,USBDIV
#fuses HSPLL,FCMEN,NOIESO
#fuses NOPUT,NOBROWNOUT,VREGEN
#fuses NOWDT
#fuses CCP2C1,NOPBADEN,LPT1OSC,NOMCLR
#fuses STVREN,NOLVP,NOXINST,NODEBUG
#fuses FCMEN BORV27
#use delay(CLOCK=48MHz)
|
You can also always see what the compiler is setting. The fuses are shown at the end of the .lst file.
You can have a lot of this set automatically for you by the 'wizard' (which if you are using MPLAB, you are not using. This though is bad since it can lead to you accepting settings that are often incorrect. Setting things manually does require you to at least partially read the manual I'm afraid. Seriously I just bought a new car, and though I knew I could get in it and drive it (had done on the test drive), I took a week to read the manual fully to know how at least some of the systems worked. There is almost nothing sold on the planet today that does not require some basic reading before use.
Before complaining about CCS. Try doing the same thing using for example MicroChip C. You'll then find how much CCS is helping you.... |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|