text:
Copyright © 2020 Microchip Technology Inc. • Legal Information • Contact Us • HI-TECH ForumSite Map
Frequently Asked Questions
About HI-TECH C® (with answers!)
You may also want to check out our on-line discussion forums.
View our MPLAB® integration page to help with the integration of MPLAB with our PICC™/PICC-Lite™/PICC-18™ compilers. These forums allow you to post and answer questions, or just browse what other people have asked or replied. If none of these questions are helpful, you may send email to email@example.com.
Answers:How do I create an array of pointers in ROM?
If the array is being placed in data memory, this would be because you have put the "const" qualifier before the "*" in the array definition, e.g. const int * aname[10]; // an array (in RAM) of pointers to ints (in ROM) This makes the const bind to the type pointed to, rather than to the array itself. Put the const keyword immediately before the array name, for example: int * const aname[10] = { ... }; // an array (in ROM) of pointers to ints (in RAM) int (* const fname[10])(void); // an array (in ROM) of pointers to functions Remember that if the object is const, you will need to provide initial values when you define the object. In some situations you may want to have a const array pointing to const data, in which case you would have 'const' both before and after the '*', e.g. // an array (in ROM) of pointers to chars (also in ROM) const char * const array[] = { "string 1", "string 2" }; The rule is: Anything to the left of the star in a pointer definition refers to what the pointer accesses indirectly; anything to the right refers to the pointer variable itself.Back to top
Why dont the %f or %ld placeholders in printf work?
If you are using an older STD compiler (not the Standard operating mode of an OCG compiler) and you have code similar to: printf("number=%f\n"); but the output is 'number=f', you need to enable printing for floating-point values. You must use the command -LF, or set the Printf field in the Global tab of the MPLAB IDE Build Options dialog to Floats+longs. This will link in a much larger version of the printf function, but one which can print floating-point values as well as plain ints. The same is true for printing long int values: The -Ll option, or specifying Long ints in the MPLAB IDE options, must be selected to print long int values. This action is not required if you are using any compiler that uses OCG.Back to top
How can I speed up my interrupt service routine?
OCG compilers handle interrupt routines (ISRs) better than non-OCG compilers. In either case, however, you can improve the speed of an ISR by keeping the code contained in the ISR to a minimum. The more registers used by the ISR code, the more context save and restoration will be required and added by the compiler. Consider moving complex tasks outside the ISR and having the ISR set a flag to indicate to main-line code that the task is required. If you are using a non-OCG compiler, avoid calling other functions from the ISR, or if you must, ensure that you only call functions that are defined in the same source module and before the ISR function. This ensures the compiler will know exactly what registers are used by the function. OCG compiler will always know the exact register usage, regardless of the program structure.Back to top
What does the PICC STD error, something like "cant find xxxx words/bytes for psect in segment BANK0", mean?
See also the general discussion of errors with memory allocation performed by the linker in the FAQ: I get an error something like "cant find xxxx words/bytes for psect in segment yyyy" This FAQ deals specifically with the PICC STD compiler and is not relevant for any other compiler, in particular, this is not relevant when using an OCG compiler in Standard mode. With PICC STD compiler there are often messages similar to: Can't find space for psect rbss_0 in segment BANK0 (error) It means that you have run out of space in RAM bank 0. The error might also mention the class COMBANK, depending on the circumstances. The common memory is shared with bank 0. The solution is most likely that some variables need to be moved into banks 1, 2 or 3. You do this by prefixing the variable declarations with a qualifier like bank1, e.g. bank1 int fred; Naturally you should group variables often accessed together in the same bank. You can't use the bank qualifiers with auto variables, functions or parameters, but it can be used with static variables. You can also use them with bit variables. Beware of pointers, e.g. bank2 char * p; is a pointer to a char in bank 2, but the pointer itself is still in bank 0. This declaration bank2 char * bank1 p; is a pointer to a char in bank 2, but the pointer is located in bank 1. Memory allocation and pointer typing is automatically configured in any compiler using OCG, e.g. a compiler operating in PRO, Standard or Lite mode. This FAQ is not relevant for these compilers.Back to top
Why do I get an "Arithmetic overflow" message?
There are many reasons why this warning may be produced when compiling. Here are a few examples that might help you understand why it occurs in your code. Example 1: If I assign 0xFFFF to an integer, I get the message "arithmetic overflow in constant expression. Why doesn't it just treat it as -1? As an int, this variable can only assume values of -32768 thru to 32767. The value 0xFFFF is equal to 65535, and is thus too big. If you really don't want signed values, use an unsigned int, or use -1, or use ~0. In general, if you want a value that has all bits set, and you don't want to be specific about how many bits that is, ~0 is good. e.g. OnLimit = ~0; will assign a value to the variable that has all bits set, for a 16 bit int this will be 0xFFFF (or -1). A macro like: #define ALL_BITS_SET (~0) can be useful too. Example 2: What about something like: #define XTAL 8000000 #define BEEP_FREQ 3200 #define BEEP_TIME XTAL/(64*BEEP_FREQ)-1 // = 38.0625 this also gives me "arithmetic overflow" when I assign BEEP_TIME to an unsigned char. The final value easily fits a char so what's going on? If you are working with a compiler that uses 16-bit ints and 32-bit longs, you need to ensure that long arithmetic is used for the whole expression. 8000000 is by default a long number, but 3200 and 64 aren't, so 64*3200 is evaluated in int length, and overflows. Use an 'L' suffix to force long arithmetic in an expression, e.g. #define BEEP_TIME XTAL/(64L*BEEP_FREQ)-1 // = 38.0625 Note the 'L' appended to 64 - this makes it a long number, and the evaluation of 64L*3200 will be done in 32-bit length.Back to top
What on Earth is a fixup error?
There are a number of fixup-related error messages that all roughly mean the same thing. They are all produced by the linker application and have the form: (476) fixup overflow referencing %s %s (location 0x%lX (0x%lX+%d), size %d, value 0x%lX) (477) fixup overflow in expression (location 0x%lX (0x%lX+%d), size %d, value 0x%lX) (1267) fixup overflow referencing %s %s (0x%lX) into %d byte%s at address 0x%lX (%s%s%s %d/0x%X) (1268) fixup overflow storing 0x%lX in %d byte%s at address 0x%lX (%s%s%s %d/0x%X) (1356) fixup overflow referencing %s %s (0x%lX) into %d byte%s at 0x%lX/0x%X -> 0x%lX (%s%s%s %d/0x%X) (1357) fixup overflow storing 0x%lX in %d byte%s at 0x%lX/0x%X -> 0x%lX (%s%s%s %d/0x%X) where the printf-style placeholders will be replaced with names and values, and the numbers in brackets are the error numbers which uniquely identify the message. The latter two messages are actually replacements for the others. They contain additional information to help you understand what is happening. You will see the other messages on earlier versions of the compiler. Most of the confusion arising as a result of this error message relates to the fact that most people do not know what the process of fixup is. That makes it difficult to understand the error. So what is fixup? When you reference a symbol in either C or assembly code, the generated opcodes ultimately produced by the assembler cannot be completed because at that time the addresses of the symbols are not known. The binary opcodes are output with zeros in the opcode value where the symbol address would normally be located. Other information is output to tell the linker how to adjust the opcodes once the addresses of the symbols are determined. The process of adjusting the opcodes with the actual numeric addresses is called fixup. Now consider the case when the symbol address should take up, say, 8 bits in the opcode, but the linker determines the address to be substituted is a value like 0x110, which is large than 8 bits in size. In this case, the fixup process has overflowed and this is exactly what the error indicates. So what causes this? In rough order of likelihood, this error is generated by: * Hand-written assembly code that did not include the required address masking * Incorrect bank qualifiers and pointers when using a PICC STD compiler - This is not relevant when using any OCG compiler * Functions or statements (e.g. switch statements) that are too big * Incorrect linker options defined by the programmer * A bug in the code generator application In order to determine the cause, you need to find out the assembly instruction that triggered the error. If you received error messages 1356 or 1357, then the job is easy. Depending on the exact cause, the file and line number information produced in the error may be valid and you can find the offending C code. However, even if this is the case, remember the error relates to an assembly instruction and to look at that in detail you will probably want to look at the assembly listing file. Assume we get the following message. main.c: 11: (1356) fixup overflow referencing psect nvBANK2 (0x110) into 1 byte... ...at 0xFDA/0x2 -> 0x7ED (main.obj 30/0x8) Open the list file main.lst and look for address 0x7ED. If you are not sure where the addresses are located, read the assembler section in the manual on understanding the list file. Once you can see the assembly at fault, you can determine if this was something written by hand or generated by the compiler and the possible cause. The other messages are produced by older compilers. The address of the instruction in the list file is also given in these messages, but note that you may need to divide this address by 2 manually. This is the case when compiling for Baseline, mid-range or dsPIC devices. Note also that if you are using a non-OCG compiler, there will be more than one assembly list file generated for multi-source projects. Check the name of the .obj file referenced in the error as to which file to examine. If writing assembly code by hand, check the device datasheet to determine the exact size of each instruction operand. When using symbols as operands, ensure that the address has been masked, if required. Often upper bits of an address relate to a bank or page number and the instruction operand is an offset into these banks or pages. Here what you should NOT do in PIC18 assembly, for example: movwf _output Here is better code: movlb (_output >> 8) ;select the bank of _output movwf (_output)&0ffh ;mask out bank selection bits in address If code sequences are too big, the goto or jump instruction can become out of range. This can trigger a fixup error. If psects are moved, then assumptions the compiler made may no longer be valid and fixup error may result. For example if a psect was assumed to be located in bank 0 memory was moved to bank 2, then a fixup error might result (among other things).Back to top
Why does #asm/endasm inline assembly near an if statement ends up in the wrong place?
The #asm construct is not syntactically part of the C program - it's handled at the pre-processing level. So if you have something like: if(cond) do_stuff(); #asm ; assembler code here #endasm what happens is that the compiler sees the if() and the statement after it, then skips ahead looking for the corresponding else -- which is absent -- but it doesn't find that out until after the #asm/endasm block, by which time it has been output -- but as part of the code conditional on the if() statement. The solution is to place a dummy statement before the #asm - this terminates the scan for the else keyword, e.g. if(cond) do_stuff(); /* null statement here */ ; #asm ; assembler code here #endasm Alternatively, use the asm("") construct -- it is like a C statement, so it interacts correctly with C control structures.Back to top
What symbol lengths are supported in HI-TECH C?
HI-TECH C compilers use a default length of 31 significant characters in C symbols (this is the minimum value permitted by the ANSI standard). This is adequate for most purposes, but if more characters are needed, the length can be changed. The -N option, represented by the Identifier length field in the Compiler tab of MPLAB IDE's Build Options dialog, can be used to change the identifier length to anywhere between 31 and 255 characters. This option also controls the length of macro names used by the C preprocessor, which also have a default length of 31. The assemblers that are packaged with HI-TECH C have no specific limit on symbol lengths -- all characters are significant, and the line length is the only limitation (typically 256 characters or more). Whatever the length of a symbol, the linker preserves the full name. Symbols are case-sensitive in all situations.Back to top
Can I define a function that will be expanded inline?
Currently, no. Inline functions are not an ANSI C feature, but the same functionality is available by using macros, e.g. here is a "function" defined as a macro to write a byte to the EEPROM on a 16C84: #define EEPROM_WRITE(addr, value) do { \ while(WR != 0) /* wait till EEPROM idle */ \ continue; \ EEADR=(addr); \ EEDATA=(value); \ do \ GIE = 0; /* disable interrupts */ \ while(GIE != 0); /* make sure it worked */ \ WREN=1; /* enable writes */ \ EECON2=0x55; /* enter password */ \ EECON2=0xAA; \ WR=1; /* start write cycle */ \ GIE = 1; /* re-enable interrupts */ \ WREN=0 /* disable writes */ \ } while (0) Note the backslashes on the end of each line except the last - these are required since the macro definition is technically only one line. Note also the 'do { ... } while (0)' that is wrapped around it - this allows you to use it just like a function call even around flow-control constructs like 'if'/'else', although the compiler might complain (depending on the warning level you have selected) about the "constant conditional" in the outermost 'while' loop control expression. Unfortunately you can't use this is you want to return a value from your macro. Beware of side-effects in parameters passed in to macros - because macro expansion uses textual replacement, this macro to find the larger of two values: #define max(a,b) ((a)<(b)?(b):(a)) will expand 'max(y++, x++)' into '((y++)<(x++)?(y++):(x++))', which will cause the larger of the variables to be incremented twice - a real function call would not have this effect. Always put parentheses around every mention of a parameter name within a macro, and around the whole macro if it returns a value: /* Note this is an example of what NOT to do */ #define max(a,b) aBack to top
How do I use interrupts on a mid-range PIC device?
Just declare a function qualified with the "interrupt" keyword. The compiler will place it in the right place, and take care of all register saving and restoring. Check the manual for more details. Here's an example of a program for a midrange PIC that uses interrupts; #includeBack to top
/* Interrupt demo for PIC; wait for button press on RB0/INT, * turn on a relay on another port bit for a period of time. * For simplicity here, literal constants are used, usually these * should be calculated with compile-time arithmetic. */ static bit RELAY @ (unsigned)&PORTB*8+7; // use this bit to drive relay static unsigned int relay_timer; // timer value for relay driver void main(void) { RELAY = 1; // ensure relay is off before enabling output TRISB = 0x3F; // Port B bits 7 and 6 are output T0CS = 0; // Timer increments on instruction clock T0IE = 1; // Enable interrupt on TMR0 overflow INTEDG = 0; // falling edge trigger the interrupt INTE = 1; // enable the external interrupt GIE = 1; // Global interrupt enable for(;;) CLRWDT(); // Idly kick the dog } void interrupt isr(void) // Here be interrupt function - the name is // unimportant. { if(T0IF && T0IE) { // if timer flag is set & interrupt enabled TMR0 -= 250; // reload the timer - 250uS per interrupt T0IF = 0; // clear the interrupt flag if(relay_timer != 0) // is the relay timer running? relay_timer--; // decrement it if(relay_timer == 0) // if it has time out RELAY = 1; // turn the relay off PORTB ^= 0x40; // toggle a bit to say we're alive } if(INTF && INTE) { // did we see a button press? RELAY = 0; // turn the relay on relay_timer = 4000; // start the timer - 4000 ticks = 1 second INTF = 0; // clear the interrupt } }
How do I access bits in a RAM variable?
You may be able to do this using some simple macros: #define testbit(var, bit) ((var) & (1 <<(bit))) #define setbit(var, bit) ((var) |= (1 << (bit))) #define clrbit(var, bit) ((var) &= ~(1 << (bit))) These will read/set/clear the bit-th bit in var. You can also define a union which contains the byte variable and a structure with bitfields that maps over the byte variable, e.g. union both { unsigned char byte; struct { unsigned bit0:1; unsigned bit1:1; // etc. } bits; } var; Using this definition means you can refer to var.byte or var.bits.bit0 for example. This type of definition is portable and ANSI compliant. You can take this a bit further: use a union to map bit "variables" onto an existing byte. First up, define a structure type with bits in it. typedef struct { unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } bitv; Now given a char variable, e.g. char myvar; define a "variable" to be a single bit in that char like this: #define mybit (((bitv *)&myvar)->b0) Now you can refer to mybit and it will access bit 0 of myvar. You can use this just like you would use a bit variable defined any other way. The code generated will be just as good as any other way. Use it as follows: if(mybit) something(); mybit = 1; To streamline the process a little, you can define a helper macro. #define _paste(a,b) a##b #define bitof(var,num) (((bitv *)&(var))->_paste(b,num)) Now defining a bit "variable" is done like this: #define x4 bitof(myvar, 4) So now x4 represents bit 4 of myvar.Back to top
How do I load the OSCCAL value for PIC devices?
With the latest versions of HI-TECH C for the Baseline devices, the oscillator calibration constant is automatically handled by the compiler-generated runtime startup code. There are several schemes employed by the devices to load the calibration constant and the appropriate method is chosen by the compiler. If you are using a windowed device, the calibration constant must be saved from the last ROM location before erasing, then reprogrammed after erasing the device. If you are using an ICE, the retlw instruction will not be present and calling the _READ_OSCCAL_DATA() macro will not work. This macro will work when running code on a part.Back to top
How do I program the PIC's ID Locations?
Some 8-bit PIC devices have locations outside the addressable memory area that can be used for storing program information, such as an ID number. The __IDLOC macro can be used to place data into these locations. The macro is used in a manner similar to: #includeBack to top
__IDLOC(x); where x is a list of nibbles which are to be positioned in to the ID locations. Only the lower four bits of each ID location is programmed, so the following: __IDLOC(15F0); will attempt to fill four ID locations with the decimal values: 1, 5, 15 and 0. Some devices will permit programming up to seven bits within each ID location. For these, and only these, devices the __IDLOC7(a,b,c,d) macro is available and can be used in a similar way.
How do I place data in EEPROM?
The macro __EEPROM_DATA() can be used to place values in the EEPROM. The macro allows for 8 bytes to be defined, but can be called more than once. For example: #includeBack to top
__EEPROM_DATA(1,2,3,4,5,6,7,8); __EEPROM_DATA(9,10,11,12,13,14,15,16); PIC32 devices do not implement EEPROM and so this macro is not relevant in this case.
How do I ensure declarations are consistent across files?
If you have a variable (or structure, or whatever) that you want to define in one source file and access in another source file, you can do that using an extern declaration. This declaration must match the definition (note the two terms used). The best way to do this is to define a header file(s) which can hold all the declarations and which can be included into each source file that needs to access the variable. Let say you have a volatile int variable called IOstate that must be accessible anywhere in the program. The file main.c, for example, might define the variable: volatile int IOstate; // definition of IOstate Now, create a header file (say vars.h) and in this file add: extern volatile int IOstate; // declaration of IOstate Note that the declaration must include any qualifiers used in the definition. Now include this header file into any source file that needs to access this variable: #include "vars.h" If vars.h is stored in the same directory as your source files, the above directive will find it and not require the preprocessor search path to be changed. You should also include the header file into the source file that actually defines the variable. HI-TECH C compilers using OCG will detect any mismatch in the declarations regardless, but in case you are not using one of these compilers, this action will ensure the compiler can detect and warn regarding any mismatch. You should never place definitions in a header file, other than defining types (typedefs) or structure/union tags etc.Back to topIf you own a HI-TECH C compiler, you can update it without having to purchase a new license. Microchip's 12 month maintenance subscription provides not only web access to new versions and patch level updates, but also priority technical support for nominated HI-TECH C compilers. The subscription is called High Priority Access. When purchasing, you will have two options: High Priority Access Restart (HPR) and High Priority Access (HPA). High Priority Access Restart is for customers who do not currently have a maintenancesubscription for their compiler. High Priority Access Renewal is offered at a discounted rate and is for customers who are supported by the maintenance subscription for a compiler and are 'renewing' it for another year. (To be eligible for 'renewal' pricing, customers must renew their HPA before their current expires.) High Priority Access subscriptions can be purchased through microchipDIRECT. After purchase you will be contacted so that your license details can be updated and you can activate any updates. For any enquiries, contact email@example.com Note that 12 months of High Priority Access is included with the HI-TECH Enterprise Edition and HI-TECH C PRO compilers.Back to top
What does the error, something like "can't find * words for psect" mean?
There are a number of memory-related error messages that all roughly mean the same thing. They are all produced by the linker application and have the form: (491) can't find 0x%X words for psect "%s" in segment "%s" (593) can't find 0x%X words (0x%x withtotal) for psect "%s" in segment "%s" (1346) can't find 0x%X words for psect "%s" in segment "%s" (largest unused contiguous range 0x%lX) (1347) can't find 0x%X words (0x%x withtotal) for psect "%s" in segment "%s" (largest unused contiguous range 0x%lX) where the printf-style placeholders will be replaced with names and values, and the numbers in brackets are the error numbers which uniquely identify the message. The latter two messages are actually replacements for the first two. They contain additional information to help you understand what is happening. You will see the first two messages on earlier versions of the compiler. These meaning of the error is simply this: The linker was asked to position a block of something (what we call a psect) in memory and it could not find a free space large enough to accommodate it. So if you have 54 words of memory remaining and you write additional code which will require another 110 words, obviously you will have exceeded the memory of the device and this error would be issued. In particular you might see something like: (1346) can't find 0x110 words for psect "text32" in segment "CODE" (largest unused contiguous range 0x54) If that was the end of the story there would not be as much confusion over this error message as there is, so we need to elaborate this a bit further. Often the question is asked "I have been developing my code and the memory summary told me I have 200h words free, but I added a couple more lines of code and now am getting this error message. What's going on?" The issue is much more complicated that just "This is the total number of words unused, and this is how many more I need." and it is mostly an issue when compiling for the 8-bit PIC devices. The trick to all this is: A psect cannot be split. The linker cannot place part of a psect somewhere and the rest somewhere else. Consider the case when there might be 200h words free, but this space is fragmented so that there are 4 areas of free memory, each 50h words in length. Now consider if the size of the psect that needs to be allocated was 60 words long. Allocating an extra 60 words when there is 200 words free sounds like this shouldn't be a problem, but if each free space is only 50 words in size, none of them will be large enough to accommodate the psect and this error message will result, e.g. (1346) can't find 0x60 words for psect "text24" in segment "CODE" (largest unused contiguous range 0x50) What is distracting is that the memory summary and MPLAB IDE memory gauge only report on the total amounts of memory used, hence the total amounts unused. This information is still useful, as it roughly indicates how full the device is, but it is of no use when dealing with complex memory allocation issues. So how is it possible that there can be four areas of 50h words free? Why wasn't everything allocated so there was one big free space left? There are a couple of answers to this question. * The data and program memory on 8-bit PIC devices (10/12/16/18) are banked or paged. * You may have allocated functions or variables at absolute addresses. When a psect is placed in memory it typically cannot be positioned so that some of it is in one page/bank and some is in another (there are some exceptions). This is usually to allow generation of much more efficient code. For example if a psect containing variables was to be placed so that it could exist in two banks, then the bank boundary could fall right in the middle of a variable. The compiler could not assume that each variable was entirely located in one bank and bank selection instructions would need to be issued before accessing each byte in the variable. The code size would dramatically increase. The situation is worse on some devices. For example the GP RAM on Mid-range and Baseline PIC devices is not contiguous -- the special function registers are located in-between each bank, so it becomes impossible to position large psects. When absolute variables or functions are defined, they are placed at the address specified by the program. If these locations fall in the middle of a bank or page, this effectively splits the bank or page into two smaller ones. The smaller the pages or banks, the harder it is to find space for all the psects. In some cases other memory must be reserved. For example if you are using an ICD for debugging, it requires memory that you could normally use for your code and variables to be reserved for the debugger code. This memory is subtracted from the available memory when compiling. If that is not bad enough, there are also some psects that have special requirements when it comes to their placement. For example a psect may need to be linked above a certain address, or it may need to be linked at an address multiple (e.g. word aligned or aligned to the nearest 100h words). This really complicates the situation. It is even possible that you may see an error message something like this. (1346) can't find 0x30 words for psect "strings" in segment "CODE" (largest unused contiguous range 0x50) which at first glance seems to indicate that the linker is not doing its job properly. Let's look at this error as an example of how to work out exactly where the problem lies. This is a more difficult case that will show you many aspects of memory allocation. What does the error actually tell us? The message indicates that the linker is currently positioning a psect called strings. This psect is 30h words long and the linker has been asked to position it in the CODE class. So what is strings? You might be able to guess, however to find out for sure check the section in the manual called "Compiler-generated Psects." This will tell you what the psect holds so you will know to what the error relates. It is pointless looking at you RAM usage if the error indicates you have run out of program memory. Strings holds characters that form part of string literals and is stored in the program memory. The name of the segment (class) is usually self explanatory, however the definition of the class is made by a linker option. Look at the linker options when doing a verbose build, or check the Linker command line section at the top of the map file. You will see the option which defines the class. Let's assume we are using a 16F877 device; it might look something line this: -ACODE=00h-07FFhx4 which says the CODE class represents memory from 0-7FFh, then from 800h-FFFh, 1000h-17FFh and finally 1800h-1FFFh, that is, there are four memory pages. This is quite different to a single memory range from 0-1FFFh, so already we have fragmented memory to work with. Many psects are allocated to the CODE class and anything that is linked in program memory will consume memory covered by this class. The map file indicates where psects have been linked as well as their length and link location. Check the space value to ensure that you are looking at psects that are linked in program memory, not RAM. Check the manual section on Map files for full information on how to read the map file. The map file will also contain a section called UNUSED ADDRESS RANGES which shows the memory spaces still unused. You might see, for example: UNUSED ADDRESS RANGES Name Unused Largest block Delta CODE 007B0-007FF 50 2 00800-00804 3 01FE6-01FFF 1A This indicates that there are several ranges still available. The first is the largest and it is the space that was referenced in the error message when it says "(largest unused contiguous range 0x50)". You can see that there are two other free spaces, but these are smaller. For example there is 1Ah words available in the range 1FE6h-1FFFh. So both the error message and the map file agree and we can see the exact memory reaming, not just the total reported in the memory summary or memory gauge. Typically at this point you might find that the memory is virtually full or that it is badly fragmented and that the psect cannot be positioned in any of the small gaps remaining. But that is not the case here. There is one contiguous range of memory, 50h words in length, but the linker cannot place the strings psect in this memory, even though it only needs 30h words. Why? It's time to turn to the psect itself. How is it linked and are there any special linking requirements? For the device in question you will not see any linker option in the map file that seems to place the strings psect. This means that the psect will be positioned anywhere in the class to which it belongs, which is the CODE class. Okay, so lets look at how the psect is defined. In the assembly list file we find the definition for this psect towards the top of the file. psect strings,global,reloc=256,class=CODE,delta=2 Check the manual for the meaning of all the assembler PSECT directive flags, but here we see strings is a global psect (is part of one big strings psect that is built up from all modules). We can see the option that actually indicates that strings lives in the CODE class. We can see that the delta for the psect is 2 (which means that it resides in memory that is word addressed). But the solution to our problem is the reloc flag, which declares that this psect must be linked at an address which is a multiple of 256 (100h). So are there any addresses in the remaining spaces that start on a 100h boundary and which are at least 30h words long? No! Okay, so now we know how to determine what the problem is. What can we do about it? If the problem is excessive program memory use, you need to reduce the size of any psect that is linked into program memory, such as executable code, const data or strings: * Ensure the optimizers are enabled * Use the smallest data types you can to reduce the size of code that accesses these objects * Use unsigned data types, if possible, as these can sometimes be easier to access * Avoid mixing types in expressions as that can introduce a lot of unnecessary type conversion For excessive data memory use, you need to reduce the size of any psect that is linked into the data memory. These are mainly variables: * Remember that unless an object is qualified as "const" it is located in data memory. large arrays and the like should be ideally placed in program memory if possible as there is more of this memory available. * Use the smallest data types you can to reduce the storage requirements of these objects * Look at using auto variables rather than globals, if possible, so that variable might be able to share storage * Use pointers to large objects rather than the objects themselves if they are passed to functions as arguments If the problem is that the memory is fragmented into many small free ranges: * Do not define absolute variables or functions - if you must, ensure that are placed at the end of a bank or page * Split large psects these into smaller ones - If the psect holds the code associated with a function, split the function into two; reduce array or structure sizes, where possibleBack to top
Do I need to do anything to debug with the MPLAB-ICDs?
If you are using the HI-TECH Universal toolsuite, make sure the Debugger field in the Linker tab of the Build Options dialog is set to Auto. With this setting, the compiler's debugger option will reflect the settings you make in MPLAB IDE's Select Tool option, under the Debugger menu. There is nothing else that you need do. You can override this by change the setting, but do so with caution. If the ICD is being used as a debugger, the compiler must reserve memory locations that your program may otherwise use. There may also be other ICD requirements that need to be met by the compiler, such as shadow register usage or NOP instructions that need to be used.Back to top
How do I position code or variables at a specific location?
There are two ways this might possibly be done. 1) Making the variable or function absolute 2) Placing the variable or function in a custom psect and linking it to the desired location Version 9.70 compilers and above an use either method. Older STD compilers can only use method 2. OCG compilers prior to version 9.70 can use method 1 for most objects. If I have a choice, which should I choose? Making an object absolute is the easiest way of specifying a link location. However you are limited to the object being at a known address. Using psects is more complex, but does allow you to use the features of the linker to allocate memory for the object. You might, for example have the linker place the object anywhere in an address range, or anywhere provided it is above a specific address or after some other object. In either case consider weather you need to do this as it immediately creates non-portable code. If you must place objects at a known location, try to place the object at a sensible location. If it is located in the middle of a bank or page, this will severely hamper the ability of the linker to position other objects and "can't find space" errors may result. 1 Absolute objects Absolute objects are created by using the "@ address" construct, where "address" is the location at which you would like the object placed. Here are some examples. int foobar @ 0x80; const int marker @ 0x2000; const char table[] @ 0x100 = { 0, 1, 2, 3, 4 }; int readInput(int a) @ 0x200 { // normal function body } Always check either the assembly list or map file to ensure that the object has been located as you expect. 2 Using psects and the Linker Using the "#pragma psect" directive you can place C code output or variables into a custom psect. You can then use linker options to place the new custom psect at the desired address. The syntax of pragma psect is: #pragma psect original_psect=new_psect where "original_psect" is the name of the standard psect the code, constants or variables were orginally in. The orignal psect can be found by looking in the assembly list file or at the symbol table in the map file. The names of psects used will vary between compilers, and may even change between compiler versions. If you determined that a function output was normally placed in a psect called text, then the pragma: #pragma psect text=extfuncpsect // function definition follows will place the function in a psect called extfuncpsect. You can then link this psect independently to other text psects. Some psects are numbered, such as some text or const psects, so they can be individually controlled. Some psects have a number in their name to indicate the bank they belong to. To represent the name of a numbered psect (other than those that contain a bank number) replace the number with '%%u'. e.g. #pragma psect text%%u=extfuncpsect // function definition follows The pragma will affect everything in the module, so create a new file for the functions or variables that are to be positioned explicitly. To position the new custom psect you will need to add an additional command line option -L-Pcustom_psect=address The -L driver option will pass the following option (in this case a -P) to the linker. The linker option -P will place "custom_psect" at "address". For example, use: PICC -16f877 main.c serial.c -L-Pfixed_serial=50h If you are compiling under MPLAB IDE, then this option should be specified in the Additional Command-line options field of the Global tab of the Build options dialog. Consult your manual for more information.Back to topThe printf() function performs two main tasks: formatting of text, and printing this formatted text to stdout. The exact location of stdout is determined by a second function called putch(), which is called by printf() to output each character. By default the putch() function is empty. It should be customised to suit the project at hand. Often printf() prints to a USART, but it could define stdout as being an LCD or SPI. Code to initialize the intended destination must be executed before printf() can be called. A sample definition of putch() can be found in the putch.c file in the sources directory of the compiler installation directory. After a copy of this file is customized, it should be included into your project.Back to top
How do I create a new project in MPLAB IDE which uses a HI-TECH C compiler?
The easiest way to create a new project in MPLAB IDE is to use the Project Wizard selectable from the Project menu. Make sure you select the HI-TECH Universal toolsuite for all modern HI-TECH compilers. The compiler location is not used and should be left as it appears in the field, even if it is wrong. You select the compiler to use for a project from the Build Options dialog once you have created the project. See the webinar: http://techtrain.microchip.com/webseminars/ArchivedDetail.aspx?Active=193 for detailed information on the toolsuite and creating projects.Back to top
What is HPA (High Priority Access)?
HPA is a support subscription, purchased in 12-month blocks, and that allows you to download updates to your compiler free of additional charge during that 12-month period. See the FAQ "How do I update my compiler?" for more informationBack to top
How do I fill unused program memory with a known value?
Modern HI-TECH C compilers implimented a --FILL driver option which allows you to do this. You can access this option in MPLAB IDE Build Options dialog: go to the Linker tab and enter the appropriate argument in the Fill field. This option utilizes the HEXMATE application to fill unused locations, thus it will only work for HEX files -- it will not fill unused locations in binary file outputs, for example. The argument to the driver's --FILL option (or the equivalent field in MPLAB IDE) are the same as the arguments to HEXMATE's -FILL option. Check your manual in the Utility section for the format of this option and for other features that HEXMATE provides.Back to top
How do I access variables in PIC18 devices' external program space?
The far type qualifier is used to place variables into external program space of PIC18 devices. Only some devices support the external memory interface. Accesses to far variables are less efficient than accesses to internal variables and extensive accesses to these variables will result in larger code sizes. Here is an example of an unsigned int object placed into the device???s external code space: far unsigned int farvar; Note that a --RAM option is mandatory to specify the external address range where far variables will reside. In MPLAB IDE, add the additional memory into the RAM Ranges field in the Global tab of the Build Options dialog. For example, specify --RAM=+30000-3FFFF to the driver, or enter: +30000-3FFFF into the RAM Ranges field in MPLAB IDE. You may also want to set the --EMI option to select which mode your device will use to access the external memory. In MPLAB IDE, adjust the External Memory field in the Global tab of the Build Options dialog. Refer to the your manual for more information relating to this option. Configuration bits and SFRs may need to be set up to configure the device to use the external memory.Back to top
Can I install two or more versions of the same compiler?
Yes. Compilers will be automatically installed into a directory that is specific to each compiler version. So under Windows, for example, the 9.63 and 9.64 compilers would typically live in separate directories. C:\Program Files\HI-TECH Software\PICC-18\PRO\9.63\ C:\Program Files\HI-TECH Software\PICC-18\PRO\9.64\ Note, however, that a patch-level update will by default try to install itself over the top of a compiler of the same base release. For example if you tried to install the 9.63PL1 compiler, it would install into the 9.63 directory, thus overwriting the previously installed compiler. If you installed 9.63PL2, it would then overwrite the 9.63PL1 compiler. If you would like to maintain separate copies of different patch levels of the same base release, you simply need to adjust the default installation directory shown in the installer. There is no problem having more than one patch level of a compiler installed. To select which compiler will actually be used when building a project under MPLAB IDE, go to the Build Options dialog and select the Driver tab. This will show all available compiler drivers and allow you to choose the one you want. Select it and move it to the top of the list. See the webinar: http://techtrain.microchip.com/webseminars/ArchivedDetail.aspx?Active=193 for detailed information on selecting a compiler and creating projects.Back to top
Where can I find the latest MPLAB IDE toolsuite plugin?
The latest MPLAB IDE toolsuite plugin (HI-TECH Universal Toolsuite) for all HI-TECH C compilers can be downloaded via the following link. http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en535448 More on the Plugin can be checked on this link. http://techtrain.microchip.com/webseminars/documents/HiTech_112108.pdfBack to top
Why do floating-point variables in MPLAB IDE have the wrong value?
MPLAB IDE's Watch window interprets all floating-point variables (whether defined as 'float' or 'double') as 32-bit values unless you manually change the properties for each one. Since the HI-TECH C compilers for PIC10/12/16 and for PIC18 allocate 24 bits for 'float' and 'double' types, unless you explicitly specify otherwise, this means all floating-point values displayed in the Watch view will be incorrect. This does not affect the operation of code, merely what values are displayed when debugging. To correct the display, right-click on the variable name in the Watch window and select Properties. Change the Size field to 24 bits. Also ensure that the Format field is set to IEEE Float (as opposed to MCHP Float) -- this is the only format used in HI-TECH C. You will need to do this for each floating-point variable added to the Watch view. Incidentally, if you meant to use 32-bit floating point types in your project, adjust the Size of Double and/or the Size of Float fields in the Global tab of the MPLAB IDE Build Options dialog and recompile.Back to top
What does the error "(1187) invalid activation request" mean?
Before you can use any version 9 HI-TECH C compilers, it must be installed and activated. If you have not activated the compiler, or the activation was not successful, this message can be produced if you try to compile. You can activate a compiler by selecting: Start -> Programs -> HI-TECH Software ->Back to top
-> Activate or trial PRO mode If you are performing an off-line activation, until you receive the activation response file and use this to complete the activation the compiler cannot be run in a licensed mode. See the webinar: http://techtrain.microchip.com/webseminars/ArchivedDetail.aspx?Active=193 for more information on activation and creating projects using the HI-TECH Universal Toolsuite.If you believe you've identified a bug in the compiler, here are some suggestions to speed a resolution. Check to ensure you are using the latest version. Customers with a valid HPA subscription can download the latest version free of charge. Updates can be downloaded from the Microchip website, here: http://www.microchip.com/ Please send any support questions to email@example.com or contact your local FAE. This is the official channels for help requests. The forum is not regularly monitored and should not be used for this purpose. In most situations we will require code to reproduce the problem you are having and devise a fix. If you are unable to cut down your project to isolate the problem, you may need to zip the entire project and send this in. When making initial contact, please make sure you indicate the compiler you are using, the compiler version number, and, if applicable, your serial number.Back to top
Why are some of my variables missing from MPLAB IDE?
An OCG compiler running in PRO mode may determine that some of your variables are never used. These variables will be removed from the program. This means they will not take up memory, not appear in the assembly list or map files, and they will not appear in the MPLAB IDE Watch view. Variables that are used, but only in a trivial fashion may also be used. For example you may assign a value to them, but not read the value later. If there is no functional effect of using the variable, it may be removed. If you want the variable to remain, despite the fact that it is not used, qualify it as "volatile" for debug builds.Back to top
Why do I get "(924) missing argument to -O option" when compiling under MPLAB IDE?
The command-line options passed to the compiler driver changed in version 9 compilers, and you need to be using a new MPLAB IDE compiler plugin to ensure these new options are used when compiling. If you're using a version 9 compiler, in MPLAB IDE choose Select Language Toolsuite from the Project menu and select the "HI-TECH Universal Toolsuite" rather than one of the older compiler-specific toolsuites, such as the HI-TECH PICC Toolsuite or the HI-TECH PICC-18 Toolsuite. Do NOT specify the path of the compiler in the Location field of this dialog; it is not used. See the webinar: http://techtrain.microchip.com/webseminars/ArchivedDetail.aspx?Active=193 for more information on creating projects and using the HI-TECH Universal Toolsuite in MPLAB IDE.Back to top
What's with all these PRO, Standard & Lite modes, compiler types, evaluation and full versions?
We have changed the way compilers are bundled. The transition period may lead to some confusion but the final product range will be simpler to understand and easier to use. For some time HI-TECH C compilers just had the one form, such as PICC and PICC-18. For some devices we introduced a separate Lite compiler, which was a separate product and a separate executable. With the Introduction of our OCG technology we introduced a new PRO compiler and the existing (non-OCG) compiler was then renamed as the STD compiler. So, for example we had a PICC PRO and a PICC STD compiler, as well as the Lite compiler. These were all separate compilers and separate executables. You had to download and install these separately if you wanted to be able to use them all. In MPLAB, to swap compilers you had to associate your project with a new compiler and rebuild. The next step of the transition was releasing just the one product and the one executable, but one which can operate in three operating modes: PRO, Standard and Lite. The Standard and Lite modes replace the STD and Lite compilers. If you owned a license for the STD compiler, that carries forward to the new Standard mode. All these modes use OCG, but the level of optimizations that are utilised vary between modes. The Lite mode continues to be available free of charge and is only limited in the efficiency of the generated code. For example the PIC10/12/16 compiler version 9.70 can operate in Lite, Standard or PRO modes. This means there is only one application you need to download and install to get access to all three modes. You can swap back and forth between modes (license permitting) simply as a build option in MPLAB IDE. All HI-TECH C compilers can be evaluated. An evaluation is a time-limited period in which the compiler can be run in the non-Lite mode without charge. There are very few restrictions other than the length of time the compiler will operate in that mode. The compiler will revert to the Lite mode after the evaluation period has ended. Customers who purchase a PRO license are entitled to use the compiler in Standard mode (as well we Lite, of course). If you need an old STD compiler for legacy code, that can usually be provided to you.Back to top
Why do my variables in the MPLAB IDE Watch view have the wrong value?
See also the FAQ "Why do floating-point variables in MPLAB IDE have the wrong value?" If this is not applicable the reason may be that the compiler has cached the variable into a register and MPLAB IDE is reading the variable's original memory location. The compiler can reduce the code size required to access a variable by storing it in a register. This cannot always be done, but it can have significant benefits. To make matters worse, the register chosen may be different at different points in the code. Unfortunately for those compiler/debugger combinations that still use COFF for debugging (all compilers except PIC32) will have this problem as COFF does not support the ability to track variables in more than one location. This does not affect the operation of the code and often you will see the Watch view indicate a wrong result, but the program operate correctly.Back to topIf you already have a HI-TECH C compiler and want to know if it supports a particular device there are a couple of ways you can do this. 1) Run the compiler on the DOS command line with the argument --CHIPINFO. For example, if you were checking the 9.70 PICC compiler use the following command. "C:\Program Files\HI-TECH Software\PICC\PRO\9.70\bin\picc.exe" --CHIPINFO If you are checking another compiler, use the installed path appropriate for that compiler. What you will see is a list of supported devices printed to the console. 2) If you have the compiler integrated into MPLAB IDE you can get the same information from the Build Options dialog. Click the Driver tab of this dialog and ensure the compiler in question is shown in the Available drivers list. Select it. The list of supported devices will be shown in the box towards the middle of this dialog. Scroll through and look for the device you are interested in.Back to top
What does the error, something like "Cant generate code" mean?
The error means that the compiler tried but was unable to generate output for the indicated C source. It usually does not indicate that the C code is invalid, but review the code to be sure. This error will most likely occur with complex expressions, particularly on devices with few working registers and simple architectures, e.g. Baseline devices. Remember that C code that may not look complicated to you may be very difficult to implement in assembly code. The solution is to try to simplify the expression, maybe using a temporary variable to hold an intermediate result. The compiler often has to resort to using compiler-defined temporary variables to hold intermediate results so this work around is often no less efficient that what would be otherwise possible. Please inform support staff of the error as the compiler can often be tweaked to allow the code to compile, but also consider this may be a warning that you asking too much of the instruction set.Back to top
What does the error "activation limit reached" mean when I try to activate a compiler?
A single-user license allows a compiler to be installed (activation is actually the important issue) on up to 2 computers. Only one copy of the compiler may be used at any given time, however. So for example, you might install the compiler on a desktop machine for day-to-day use, and also install it on a laptop for field work. If you attempt to install and activate the computer on more then two computers you will see this error message appear. If you have had to reinstall the compiler several times and see this error, you can contact support staff and explain your situation. Note that the record of your activations is not permanent. After a time, it is reset and further activations can be made. This allows you to update your computer or operating system without having to contact support.Back to top
How can I reserve memory and prevent it be used by the compiler?
When you compiler for a particular device, all the on-chip memory ranges are known by the compiler. If required, you can reduce these memory ranges, and providing the device allows it, specify additional memory to these ranges. These adjustments can be made using the --RAM and --ROM driver options. If you are using MPLAB IDE, these options have direct counterparts in the Global tab of the Build Options dialog. Here the RAM ranges and ROM ranges fields should be used to specify the adjustments. The arguments required for the driver options and the fields in MPLAB IDE are identical. You should consult your manual for full details of the arguments. A typical requirement is removing a range of memory. To do this, you can use: --ROM=default,-1000-11ff or enter: default,-1000-11ff into the MPLAB ROM ranges field, for example, which will remove the range from 1000h to 11ffh from the default (on-chip) ranges.Back to top
Do you support the PIC17 devices?
PIC17 devices are only supported by the older STD compiler. New compilers using OCG -- either PRO, Standard or Lite modes -- do not support these devices. PICC STD version 9.60 PL3 was the last official compiler release to support these devices. If you own a license for a later compiler, you are entitled to use this compiler.Back to top
Can HI-TECH C check for MISRA compliance?
No. You will need to see if there is a static code analysis tool available, such as PC-Lint, that can do this for you.Back to top
What are the restrictions in Lite mode?
Essentially none. Modern Lite mode compilers can compile for the same devices as Standard and PRO mode compiler, and each device will have present its full amount of memory. Lite mode compilers even use OCG and so the entire C program is compiled in one step and the source code does not need many non-standard extensions. The main difference when using Lite mode is that not many optimizations are employed and so the code size will be significantly larger than when using Standard or PRO modes. There are only a couple of command-line options disabled in Lite mode, but these do not relate to code features; merely how the compiler can be executed. Most customers never need to use these options.Back to top
How can I change license details of a compiler?
Most of the information associated with a license (serial number) can be change on the HI-TECH website. You will need the user ID and e-mail address that was sent to you when you registered the product (or it may have been sent if you ordered a downloadable compiler online). If you own more than one license that is registered under the same name, then you will most likely have one ID for all licenses. The login page can be found here: http://www.htsoft.com/downloads/log_in.php and then proceed to the "Your details" page.Back to top
What does the error "invalid version number" mean when I try to activate a compiler?
This error indicates that you are not entitled to use this version of the compiler. If you have purchased a license for a compiler, you are entitled to any patch-level updates to that version. That is, if you purchased a 9.60 PIC STD compiler, you are entitled to use -- and for no additional charge -- 9.60 PL1 or 9.60 PL2 etc. of the STD compiler, if they are made available. If you have a valid HPA (High Priority Access) subscription, you are entitled to use any update to the compiler you purchased which is released during the subscription period. If the update is not a patch level update to a base release version you already own, or you do not have a HPA subscription, then you will need to purchase a HPA restart subscription to be able to activate the new compiler. If you believe this message is in error, please contact firstname.lastname@example.com and quote your license details.Back to top
What does the error "invalid activation response" mean when I try to activate off-line?
If you are activating off-line and you receive this error it is most likely that the activation request file does not correspond to the machine being activated or that the request file you sent via e-mail was altered by your e-mail client. The activation request file you send in must be generated by the machine that is to run the activated compiler. The request file contains information specific to the Windows installation on the machine and is only valid for that machine. You should regenerate the activation request file any time you need to reactive to ensure that the data it contains is accurate. If you sent the contents of the file in the body of an e-mail message and you have set your e-mail client to send in HTML format, this would explain the error. The activation program is expecting plain text for any received requests. If you send the request file as an attachment, this will work around this issue. Also check no unusual end-of-line characters could be sent in the request file.Back to top
How many computers can I install the compiler on?
A single-user license allows a compiler to be installed (activation is actually the important issue) on up to 2 computers. Only one copy of the compiler may be used at any given time, however. So for example, you might install the compiler on a desktop machine for day-to-day use, and also install it on a laptop for field work. If you attempt to install and activate the computer on more then two computers you will see an error message appear indicating that your activation limit has been reached. If you have had to reinstall the compiler several times and see this error, you can contact support staff and explain your situation. Note that the record of your activations is not permanent. After a time, it is reset and further activations can be made. This allows you to update your computer or operating system without having to contact support.Back to top
Does HI-TECH C support any binary object files or libraries in formats other than HI-TECH's?
Currently, no. The object files and library file formats are HI-TECH specific and the compiler cannot read any other formats.Back to top
How can I use the TO and PD bits to determine the cause of reset on PIC devices?
Some PIC devices have TO and PD bits in the STATUS register that can be used to determine the cause of a reset. However the state of these bits are soon overwritten after the reset has occurred and program execution has resumed. The --RUNTIME suboption "resetbits" can be used to preserve these bits, and the entire STATUS register, in variables that can be examined later in your program. If you are using MPLAB IDE, this option can be specified by enabling the Backup reset condition flags checkbox in the Linker tab of the Build Options dialog. For full details of the names and types of the variables used, check your compiler manual in the section called Status Register Preservation.Back to top
Are administrator rights needs to use the compiler in MPLAB IDE?
Users do not need to have administrator rights to access the compiler from within MPLAB IDE, but the MPLAB plugin (Universal toolsuite) does need to be able to read and write certain entries in the registry. If your user account does not have administrator rights, then you will need to ensure that the registry entry for: HKEY_LOCAL_MACHINE -> SOFTWARE -> HI-TECH Software has "Full control" set for this any folder and all subregistry folders. In the Windows Regedit program, right click on the above folder and choose Permissions. Select the appropriate Group or user name and review the Permissions.Back to top
What does the error "no file arguments" mean?
This indicates that the compiler was run but with no source files. If you are using MPLAB IDE, make sure the source file(s) have been added to your project. Having the file open in the MPLAB text editor does not mean the file is in the project. If the file is open, right click in the file and select Add to project. If you are using the command line, ensure the name of the source file(s) are listed on the command line along with any compiler options.Back to top
Why dont checksums or fills work when generating a binary output?
Some compiler features, such as those to add checksums or to fill unused memory locations, are fulfilled by the HEXMATE application, which only works with HEX files. If you are generating a binary file output, then this file will not contain any codes inserted by HEXMATE. If you do require a binary output and these additional compiler features, then it is best to generate a HEX file and then use a third-party application to convert the HEX file to a binary format.Back to top
Does the same serial number work with Windows, OS X and Linux compilers?
Yes. Normal licensing conditions apply, in that you can only activate up to two compiler and only one may be in use at any given time, but there are no restrictions as to the platform on which the compiler can be used.Back to top
Does the HI-TECH C compiler for PIC18 MCUs support the extended instruction set?
Currently, no. The extended instruction set allows the device to access additional instructions, but at the expense of loosing access bank memory. The main reason for the use of this instruction set is to allow reentracny, which is currently not supported by the compiler. Reentrancy may be introduced in future versions and this may well require utilization of the extended instruction set.Back to top
What are the nesting limits of function calls on 8-bit PIC devices?
For PIC18 and mid-range PIC devices, the function call depth is the same as the depth of the hardware return address stack. Check the datasheet for the device you are using. Baseline PIC devices have a very small hardware stack depth (2 calls) and to work around this, the compiler can employ a call mechanism based on lookup tables. This allows a much larger level of function nesting, with no theoretical limit. Use of the alternate call mechanism is automatic. The call graph produced by the compiler (shown in the assembly list file for compiler versions 9.70 and above, or the map file for earlier compilers) can be used as a guide to stack depth, but this shows only a static analysis and so may not reflect actual usage. See your compiler manual for more information on function calls, and the fastcall qualifier.Back to topFirst, libraries cannot be created using MPLAB IDE; you must use the command line to build the file. You can use the command line shown in the MPLAB Output Build windows as a guide to the command line you should use to build at the DOS command prompt. Second, you must built the correct library type: LIB or LPP. If you are using a compiler with OCG and the source code for the library files is written in C (as opposed to assembly), then you must choose the LPP output type. For all other cases, choose the LIB type. To check if your compiler uses OCG, go to the Driver tab in the MPLAB Build Options dialog. Select the compiler in question from the Available drivers list. Check to see if Omniscient Code Generation is mentioned in the Selected driver information and support devices section in the middle of the dialog. To generate the library file use the option: --OUTPUT=lpp or: --OUTPUT=lib depending on the library type you require. When building the library, make sure that the --ASMLIST option is NOT used. If you want to keep the contents of the library file confidential, use the --SHROUD option, which will obfuscate the modules used.Back to top
Is there any limit to the size of objects I can define?
Obviously the amount of memory implemented by the device is the ultimate limit, but due to the way things are necessarily linked into memory and fragmentation of memory, there may be additional restrictions in the size of objects. It may be possible you receive memory errors even with some memory remaining. When compiling for 16- and 32-bit devices there are few limits: * The auto data to a function cannot total more than 32k for a function when using 32-bit devices. * 32 bit devices are also limited to 64k for non-const non-auto data * One PSV page is used with 16-bit devices for storage of all const data. The biggest limitations come with the 8-bit devices: * There is also no actual function size limit for any new 8-bit compilers. But note for the PIC10/12/16 compiler (Baseline and mid-range parts) functions that do cross pages may be slightly less efficient due to longer call and goto code sequences that must be employed. This is not the case for PIC18 and enhanced mid-range devices. * In terms of data objects, all objects must be be able to fit into the general purpose memory in one data bank. (With midrange and baseline devices, some of each data bank is taken up by special function registers.) Again, the exception is for PIC18 and enhanced mid-range PIC devices which can define objects larger than one bank in size. The linker never deals with placement of variables; it only deals with placement of blocks of variables. Like everything else, variables are placed into a psect (section) which is linked as a whole. A psect cannot be split, which can mean that it may not be able to be located when there are only small gaps remaining in memory. See also the FAQ called "What does the error, something like "cant find xxxx words/bytes for psect in segment yyyy", mean?"Back to top
How do I interpret the call graph in the assembler list file?
With second-generation OCG compilers (typically version 9.70 and above) the call graph is shown in the assembly list file. With older compilers this graph is shown in the map file and, although it represents similar information, is presented in a different form. The call graph shows the call hierarchy associated with functions in a program, i.e. which functions are called by a function. Following is an example of a call graph for a simple program. The numbers on the left are just the line numbers in the file and have no other meaning. 445 ;Call graph: Base Space Used Autos Args Refs Density 446 ;_main 1 0 752 0.00 447 ; 7 BANK0 1 448 ; _init 449 ; ___wmul 450 ; _wakeup 451 ; _ctrl_s0 452 ; _ctrl_s1 453 ; _null_funct 454 ; _inpSWITCH_s4 459 ; _inpADC_s0 460 ; _inpADC_s1 461 ; ___bmul 464 ; _inpADC_s0 0 0 40 0.00 477 ; _next_state 478 ; _inpADC_s1 0 0 50 0.00 479 ; _ADC_start 480 ; _next_state 481 ; _get_sec_state 502 ; _ADC_start 2 0 10 0.00 503 ; 0 COMMO 1 504 ; 0 BANK0 1 Shown in this call is call hierarchy as well as information relating to the placement of auto and parameter variables. * Base: The start address of this block in the compiled stack * Space: The memory space in which this block of the compiled stack will reside (data/program etc) * Used: How many bytes were used in this space * Autos: The number of autos and temporary variables defined in this function that use memory in the compiled stack * Args: The number of parameters defined in this function that use memory in the compiled stack * Refs: The total number of references to local objects in this function * Density: Is not currently implemented main() is the root of a call graph (note that the assembly form of the function name, _main, is used). On line 446 it is shown along with a summary of objects it defines (autos plus parameters, number of references) in lines 446 and 447. Here we see that an auto variable was allocated to that block of the compiled stack that resides in bank 0 RAM. It is placed at an offset of 7 in this block of the compiled stack. Lines 448 to 461 show all the functions that main() calls, or may call. Then for each of the functions that main() calls, their information in shown. Line 464 shows that inpADC_s0() does not define any local objects (hence there is no breakdown of where objects are allocated), but this function does call next_state(). The process continues. For our example function ADC_start(), you can see it defines 2 bytes of auto and temporary objects. One byte is allocated to bank 0; the other to common memory. Your compiler manual has a full description of the call graph in the Assembler chapter.Back to top
How do I interpret the pointer reference graph in the assembler list file?
The pointer reference graph is shown in the assembly list file for compiler versions 9.70 and above. This graph shows every pointer in your program, along with each target the pointer can reference. Here is an example. The numbers on the left are just the line numbers in the file and have no other meaning. 433 ;Pointer list with targets: 434 ;task_st_tab const PTR FTN()void [8][4] size(1); Largest target is 0 435 ; -> ctrl_s0(), ctrl_s1(), null_funct(), 436 ; -> inpSWITCH_s0(), inpSWITCH_s1(), 437 ; -> inpSWITCH_s2(), inpSWITCH_s3(), 438 ; -> inpSWITCH_s4(), inpADC_s0(), 439 ; -> inpADC_s1(), outLEDS_s0(), outLEDS_s1(), 440 ;task_ptr PTR struct task_def size(1); Largest target is 36 441 ; -> task(BANK0[36]), 442 ;timer_intr@req_ptr PTR unsigned char size(1); Largest target is 1 443 ; -> task.ctrl(BANK0[1]), 444 ;timer_intr@task_tmr_cnt_ptr PTR unsigned int size(1); Largest target is 2 445 ; -> task.timer_count(BANK0[2]), The top line shows the pointers name, and type. The size of the pointer is indicated. Remember that this size is determined from how the pointer is used, not from its definition. The largest target is also shown. This is important when determining the pointers size as a pointer may be required to increment over an array, for example, and so must be large enough to access all addresses occupied by the array. The name of each target is printed along with the memory class in which it is located, and its size. For example, we can see that task_st_tab is an array of pointers to a functions. The address of this array is 1 byte wide. All the functions that the array elements can reference are listed. task_ptr is a pointer to a strcture. This pointer is 1 byte wide and it only ever points to a structure called task. The structure is 36 bytes wide.Back to top
Does HI-TECH C support the use of the malloc-style functions?
HI-TECH C compilers for 16- and 32-bit devices support dynamic memory allocation functions like malloc etc. 8-bit compilers do not support these functions. As these devices have small amounts of RAM and this memory is banked, it is totally inappropriate to implement dynamic memory allocation on these devices.Back to top
Where I can find information about compiler messages?
All error, warning and advisory messages have a unique number which is typically displayed at the start of the message. For example: example.c: 5: (751) arithmetic overflow in constant expression (warning) The unique message number here is 751. This number can be used to control the behaviour of the message using a number of compiler options, or the #pragma warning directive. All messages are documented in the "Error and Warning Messages" chapter of the user manual. This documentation will also indicate which compiler application produced the message -- it is not much good looking for a problem in C code if the message is produced by the assembler, for example.Back to top
Why don't extended ASCII characters work in 9.70 compilers?
To comply with ANSI standards, 9.70 compilers no longer support the extended character set in const char arrays. Instead, they need to be escaped using the backslash character, as in the following example: #includeBack to top
void main(void) { const char name [] = "Bj\xf8k" ; printf("%s's Resum\xe9",name) ; \\ will evaluate to "Bjørk's Resumé" } The ANSI table of extended characters can be found at the following website: http://ascii-table.com/ansi-table.php A helpful guide on C character sets can be found at: http://www.dinkumware.com/manuals/?manual=compleat&page=charset.html
Why don't the line numbers in an error message match the actual line with the problem?
In many cases an error or warning is reported for a line that follows the line that contains the error. For example, consider these lines; the first is missing a semi-colon at the end: input = read() input += offset; There will an expression error produced, but the error will indicate the second line. This is reasonable as the C language allows statements to be spread over multiple lines, so there is nothing actually wrong with the first line of code. Once the second line is parsed, it then becomes obvious that something is wrong. The error will be flagged at the line at which things first go wrong. If you are seeing error messages that are out by a number of lines, one thing you can check is that your source files are using the correct "new line" characters. If you are compiling under Windows, there should be CR-LF sequence at the end of each line. If, for example the LF character was stripped somehow from a line, IDEs, like MPLAB, would still interpret the remaining CR as a new line but the compiler will not. Since it is legal to have two statements on the one line, seeing a multi-statement line is not flagged as an error, but if there is an error later in the code, the line number count will not be accurate. To see the characters used by a source file you will need to use a text editor that can display them or dump the file in binary.Back to top
When migrating to OCG what do I need to do to allow C variables to be accessed by assembly code that assumes these variables are in certain banks?
Bank qualifiers are not normally needed when writing program for an OCG compiler, however if there is assembly code that assumes these variables are in a certain bank then the C variables must be qualified using the bank qualifiers (bank0, bank1, bank2 etc). Note that the bank0 qualifier did not exist in older STD compilers (bank 0 was the default destination), but OCG compilers do have a bank0 qualifier, which must be used if required. The second thing that must be done is to ensure the qualifiers are honoured by setting the --ADDRQUAL option to "require", or set the Address qualifiers selector to "Require" in the Compiler Tab of the MPLAB IDE Build Options dialog. Without this, qualifiers will be ignored by the compiler. If the variables are not qualified and are placed in banks other than those expected by the assembly code, fixup errors may occur, or the code may fail. Projects are more portable if the assembly code always selects the bank of any object and applies the correct file address mask. In this way, there are no assumptions about the location of the objects accessed and the C variables do not need to be qualified. This FAQ is relevant for customers migrating to, or from, an OCG-based compiler. Check the "Selected driver information and supported devices" field under the Driver Tab in the MPLAB IDE Build Options dialog to see which compilers are based on OCG.Back to top
When writing code that is to be portable between OCG and non-OCG compilers how do I define absolute variables that may be used in multiple source files?
If you are using the SFR variable definitions (which are absolute variables) in the header files that come with the compilers, then there is nothing you need to do. The header files that ship with the compiler are updated to ensure they are compatible with that compiler version. If you have defined you own header files, if you have copied and modified a compiler header file, or if you simply defined absolute variables and use these variables in multiple source files, then there may be changes required if you need the same code to compiler on both OCG and non-OCG compilers. For non-OCG compilers the variables must be defined in each source file as being static, e.g. static volatile unsigned char MY_REG @ 0x30; Errors relating to the variables being defined more than once may result otherwise. For OCG compilers you can use either static, extern or no storage qualifier. This FAQ is relevant for customers migrating to, or from, an OCG-based compiler. Check the "Selected driver information and supported devices" field under the Driver Tab in the MPLAB IDE Build Options dialog to see which compilers are based on OCG.Back to top
Do I need to do anything to source code that contains a #pragma psect directive when porting this code to an OCG compiler?
The #pragma psect directive was present in non-OCG compilers to allow C objects to be placed into user-defined psects which could then be linked at locations specified by the programmer. In early versions of OCG compiler (prior to version 9.70) the #pragma psect directive was not implemented, but was re-implemented in versions 9.70 and above. OCG-compilers do allow objects to be defined as absolute, thus bypassing the need to use this pragma in many instances. If you are using a version 9.70 compiler, you can continue to use this pragma, although the name of the default psect may have changed. (To check this, comment out the pragma, compile and check which psect the object is normally placed in using either the map or assembly list files.) Alternatively you can make the object absolute. See your manual for more details, but the following illustrate examples of absolute objects. int input @ 0x10; // place input at address 10h const data[] @ 0x2000 = { 0, 1, 2, 3 }; // place array at 2000h in program memory int myFunc(int a) @ 0x500 { // function entry point located at 500h } This FAQ is relevant for customers migrating to, or from, an OCG-based compiler. Check the "Selected driver information and supported devices" field under the Driver Tab in the MPLAB IDE Build Options dialog to see which compilers are based on OCG.Back to top
Do I need to do anything to a project that uses a make file or build script when porting this to an OCG compiler?
If you are not compiling using an IDE then there may be changes required to the build process. Your IDE should handle the new build process, but it is always prudent to confirm the Build Options if a project swaps to a new compiler. If you are using a two-step compilation process (i.e. compiling each source to an intermediate file (step 1), then linking (step 2)) then there are things you need to be aware of. The intermediate file format used with OCG compilers is a p-code file, using a .p1 extension and created using the driver option --PASS1. Non-OCG compilers use a traditional intermediate file format of an object file, using an .obj extension and created using the driver option -C. Some compiler options are not required for OCG builds, for example the --CP option (some pointer sizes), -Lx (printf libraries) and -Bx (memory models). Most other options have the same meaning in both compiler types. If the project uses user-defined libraries these will need to be updated. See the dedicated FAQ regarding this. This FAQ is relevant for customers migrating to, or from, an OCG-based compiler. Check the "Selected driver information and supported devices" field under the Driver Tab in the MPLAB IDE Build Options dialog to see which compilers are based on OCG.Back to top
Do I need to do anything to a project that includes user-defined libraries when porting this to an OCG compiler?
The library file format for non-OCG compilers is the .lib file, which consists of separate .obj files packaged together into an easily accessible bundle. This same file format is used by OCG compilers, but for assembly source code only. If the library is build from C code routines then an alternate library format must be used. The new format supported by OCG compilers is the .lpp file, which consists of separate .p1 files packaged together in the same way as .lib libraries. These new library files can be built in a similar way to the older style (either using LIBR directly or simply using the compiler driver and the --OUTPUT option. MPLAB IDE cannot be used to create libraries.) See your manual for more details and see also the FAQ regarding using a make file or build script for information on the file formats. Do not mix the source code used to build a library. The .lpp libraries must be built from C source only; .lib libraries must be built from assembly source only. The .lpp libraries are passed to the code generator application; .lib libraries are passed to the linker application. Regardless of the source code used to generate your libraries, you must rebuild the libraries to use them in the OCG compiler. This may be true when migrating any project to any other compiler version. If your original library source code is written in C, then you must use the new .lpp file format for the libraries; libraries built from assembly code must still use the old library format. The assembly code itself will almost certainly require modification when porting to an OCG compiler. See other FAQs regarding these requirements. This FAQ is relevant for customers migrating to, or from, an OCG-based compiler. Check the "Selected driver information and supported devices" field under the Driver Tab in the MPLAB IDE Build Options dialog to see which compilers are based on OCG.Back to top
What do I need to do to assembly code that references a function's auto or parameter block symbol ?a_xxxx or ?_xxxx when porting this code to an OCG compiler?
Due to changes in the compiler it was necessary to change the ?a_ symbols. The new symbols begins with ??_. What the symbol represents is identical for pre 9.70 compilers. For example assembly code which accessed the symbol ?a_readInput would now access ??_readInput. The ?_ symbols were untouched. Compilers versions 9.70 and above removed the restriction of having the compiled stack located in one block -- they allow the stack to be allocated into multiple memory areas (such as multiple banks) and so one symbol cannot be used represent this base address any longer. For these compilers use the alternate form of addressing for all auto and parameter objects. For example, to access the auto variable "i" defined in the function main, use the assembly symbol main@i. This FAQ is relevant for customers migrating to, or from, an OCG-based compiler. Check the "Selected driver information and supported devices" field under the Driver Tab in the MPLAB IDE Build Options dialog to see which compilers are based on OCG.Back to top
What do I need to do to assembly code that references 'btemp'-type temporary variables when porting this code to an OCG compiler?
The temporary variables defined by non-OCG compilers (btemp, wtemp, ttemp and ltemp) were usually placed in un-banked memory (if possible) and were used by the code produced by the compiler to hold some function parameters, many return values and intermediate results of some expressions. OCG compilers may not use these variables at all, or use them in a lesser role. They are now never used for function parameters or return values; instead memory is allocated in the auto-parameter block for those functions which require these variables. If assembly source code uses these symbols and the code can be called from, or calls C code, then the symbols specified in the calling convention must be honoured. Specifically, this will be the case if these symbols are used in relation to function parameters (either loading parameters for another function, or reading the parameters of its own parameter area) or in relation to function return values (either reading the return value from another function or loading the return value into its own return location). Check your compiler manual for more details on the calling convention employed. If the "btemp" variables are being used for any other purpose it is recommended that you choose different names and actually define the symbols yourself, if you are not already doing so. This will ensure that there is no interaction with the variables used by the compiler and will ensure that memory is reserved for your variables even if the compiler does not perform this task. This FAQ is relevant for customers migrating to, or from, an OCG-based compiler. Check the "Selected driver information and supported devices" field under the Driver Tab in the MPLAB IDE Build Options dialog to see which compilers are based on OCG.Back to top
What do I need to do to assembly code that reserves data memory when migrating this code to an OCG compiler?
As the code generator is involved in memory allocation of data memory objects, it must be aware of any memory that will be required by assembly code. As the code generator does not read assembly code this information is provided to it by the compiler driver which scans the assembly files before calling the code generator. The driver will scan assembly modules for any psects that are located in data memory, and which are both flagged as being abs and ovrld. The memory requirements of these psects can be easily determined (without the linker having to be run) and this information passed to the code generator. These memory locations will then not be used by any objects defined in C code. Here is an example of a psect that contains a directive to reserves 10 bytes of memory at address 110h. PSECT myAsmObjects,class=BANK2,abs,ovrld,space=1 ORG 110h ; an ORG makes sense in an abs-ovrld psect myObject: DS 10 ; reserve 10 bytes The space flag must be set to 1 for RAM-based memory. The class should ideally match an existing class (see the map file for a list of all classes defined for the device), but is not mandatory. No linker options are needed to link this psect as it is absolute (always linked at address zero). Always check the map file to ensure your code worked as expected -- check the address of the symbol myObject, for example, in the symbol table.Back to top
What does 'No valid installed HI-TECH compiler drivers' when building with MPLAB IDE?
If you have installed, activated and been using your HI-TECH C compiler and this message has just occured, the most likely reason is that you have changed the target device for the project and the new device is not supported by the installed compilers.Back to top
What could cause corrupted variables or code failure when I am using interrupts?
If you have a variable used in both main-line and interrupt code, make sure it is qualified "volatile". This will prevent the compiler from using cached copies of the variable and performing other optimizations. The compiler will also attempt to access the variable atomically, but this is not always possible if the assembly instruction set does not permit this. You can check the assembly list file to see the assembly code used to access variables and determine if the access is atomic. If it is not, consider disabling the interrupts when it is accessed in main-line code.Back to top
What could cause pins in a port to change from values I assign to them?
If you are using the port as an output, make sure that you configure it to be digital, not analog. If it is set to analog, writing to one port pin can change the state of other pins. Check the datasheet for the device you are using for more information on how to set up the port. Note also that some pins are multiplexed between the ports and other peripherals and so may not be available as I/O if you are using those peripherals.Back to top
What could cause glitches on an output port?
If you have defined your own variables to reside at the port locations, make sure you qualify these variables as "volatile". Without this, bits within the port, or the entire port byte value itself, may not be accessed atomically (in one instruction). For example, the most efficient way to write a bit on 8-bit devices is to set the bit, then optionally clear it if required. this takes the least amount of code, but can lead to the bit changing state twice. There could also be problems with cached copies of the port value being used if you should read from the port. For example: volatile unsigned char LEDBANK @ 0x00C; // this maps to PORTA If you are using the definitions for the ports that become available after includingBack to top
then these are already correctly qualified and you do not need to modify these definitions.
Where can I find the call graph produced by OCG compilers?
The latest 9.70 version compilers now place the call graph in the assembly list file rather than in the map file. This is because the call graph is now produced by the code generator, not the linker application. The information contained in the new call graph is very similar to that which was contained in the map file, however the format has changed and more information is provided. The --CALLGRAPH option (or the Callgraph selector in the Linker tab of the MPLAB IDE Build Options) no longer has any function with the new compilers.Back to top
Do I need to do anything to C source code that contains a #pragma interrupt_level directive when porting this code to an OCG compiler?
The exact operation of this pragma has changed between non-OCG compilers and OCG compilers, but the pragma is used to convey the same meaning and typically code does not need to be changed when migrating. This pragma would be used with non-OCG compilers to indicate that a function is allowed to be called from main-line and interrupt code because the programmer has taken special steps to ensure that there will be no reentrancy issues. The pragma suppresses the error message that would otherwise occur. When using an OCG compiler, this pragma is used to indicate that the function duplication that would normally take place when a function is called from main-line and interrupt code should not be performed, again due to the fact that the programmer must have taken steps to ensure that reentrancy issues have been dealt with.Back to top
How do I fix errors relating to 'illegal conversion of pointer types' or 'illegal conversion of integer to pointer'?
The latter message will be produced if you convert an integer (either literal or variable) to a pointer type, for example: int * ip = 0x1234; If you cast the type of the integer in the above, so you have: int * ip = (int *)0x1234; the warning will go away, but the problem will still occur. (This is the problem with casts -- they mask all manner of problems in code. See the FAQ "When should I cast expressions?") See the FAQ "What do I need to do to C code that assigns an integer to a pointer when migrating this code to an OCG compiler?" for more information on how to fix code like this.Back to top
When should I cast expressions?
Expressions can be explicitly case using the cast operator -- a type in round brackets, e.g. (int). In all cases, conversion of one type to another must be done with caution and only when absolutely necessary. Consider the example: unsigned long l; unsigned int i; i = l; Here, a long type is being assigned to a int type (the assignment will truncate the value in l). The compiler will automatically perform a type conversion from the type of the expression on the right of the assignment operator to the type of the lvalue on the left of the operator. This is called an implicit type conversion. You should not use a cast in this expression. The compiler knows the types of both operands and will perform the indended operation. If you do use a cast there is the potential for mistakes if the code is changed. For example, if you had: i = (int)l; the code will work the in the same way, but, if in future, the type of i is changed to a long, for example, then the programmer must remember to adjust the cast, or remove it, otherwise the contents of l will continue to be truncated by the assignment. Only use a cast in situations where the types the compiler will use are not the types that you would like it to use. For example consider the result of a division assigned to a floating point variable: int i, j; float fl; fl = i/j; In this case integer division is performed, then the result is converted to a float format. So if i contained 7 and j contained 2, the division will yield 3 and this will be implicitly converted to a float type (3.0) and then assigned to fl. If you wanted the division to be performed in a float format, then a cast is necessary: fl = (float)i/j; for example (either i or j can be cast before the division). The result assigned to fl now be 3.5. An explicit cast may suppress warnings that might otherwise have been produced. This can also be the source of many problems. The more warnings the compiler produces, the better chance you have of finding potential bugs in your code.Back to top
What do I need to do to C code that assigns an integer to a pointer when migrating this code to an OCG compiler?
The ANSI Standard indicates that integer constants (either literals or integer non-pointer variables) may be assigned to pointers, but what happens after that is implementation defined. That is to say, it is not clear what will happen if you dereference the pointer or perform other operations on it. You will also notice in the Standard that pointers are only considered valid when they point to an object. You can be guaranteed that a pointer points to an object if you assign it the address of an object (using the unary & operator) rather than assigning an integer value. Non-OCG compilers work in a very basic fashion with regard to pointers and some code that may work on these compilers may fail in OCG compiler, as predicted by the Standard. The OCG compilers perform a number of optimisations that are based on information obtained from each pointer's target list. These optimisations can be defeated if the information regarding targets is not complete. Regardless of which compiler you use, always take the address of an object when assigning to a pointer. In some instances it may be necessary to create a dummy object whose address can be taken, if no other object exists. The information the compiler needs to fully understand a pointer target is: * the base address of the target * the size of the target * the memory space in which the target lies; and * the type of the target Let's look at special examples where you might be tempted to assign integers to pointers. Let's say you want to use a pointer to calculate a checksum of all locations in bank 2 data memory (from addresses 110h to 16fh) on a mid-range PIC device. We assume here that every memory location holds a significant value, but that there could be many C objects that reside in bank 2, and we are not sure which object these are. First create a dummy object which does reside at the addresses of interested and in the desired memory space: extern unsigned char ram_dummy[0x60] @ 0x110; // defines type, size, memory space and address The object is made extern so that it does not have memory reserved for it, and being absolute, it can exist "over the top" of other objects in this bank. Form this object the compiler can determine all four of the above attributes. Now you can use this object: const char * cp; // cp is made const since we should not use it to write cp = &ram_dummy; // address taken and assigned while(cp != &ram_dummy[0x60]) // pointer comparison total += *cp++; // pointer deference Note the pointer is assigned an address which is derived from a C object. Note also the pointer comparison in the while() statement. The ANSI standard also indicates that addresses may only be compared when they point to the same object. Again, you should compare pointers with the address of some object, not an integer constant cast to be a pointer. If you wanted to do a similar thing, but with objects in program memory, the procedure is the same. Note that not all devices can directly read their own program memory, and may implement program memory which is word-addressable. If arrays are located in this memory, the values read using a pointer will be the data values in the array. You could not use this method to calculate a checksum of the instructions in a block of program memory. You would need to use flash read function to do that, if they exist for the target device. Let's say we want to read from address 2000h to 2fffh, and assuming there is no single C object that resides at these addresses, first create a dummy object which is located here and in the desired memory space: extern const unsigned char dummy[0x1000] @ 0x2000; // defines type, size, memory space and address Here the const qualifier implies the program memory space. As there is no initial values, this object does not contribute to the output hex file and so will not overwrite any other objects. Now you can use this object: const unsigned char * cp; //cp is made const since we should not use it to write cp = &dummy; // address taken and assigned while(cp != &dummy[0x1000]) // pointer comparison total += *cp++; // pointer deference OCG compilers do understand the special case of an assignment of 0 (zero) to a pointer. This is a null pointer, has special meaning, and is an address that is guaranteed not to point to any object. If you need to take the address of an object defined in assembly code, then write an appropriate C declaration for the assembly object, then use this C object as the target.Back to top
Can I access C pointers from assembly code written for an OCG compiler?
Pointer variables defined in C code will have a size and format which is determined by the compiler after analysing the entire C project. This pointer sizes and formats can change from one build to another as the C program is developed. This would make it very difficult to access C pointers from assembly code as the size and format is not fixed. The 9.70 (PIC10/12/16 9.70PL1 introduced this for mid-range devices) compilers will fix the pointer class so that it has maximum size and can access all objects from all memory spaces. For all PIC10/12/16 devices, the size of these pointers is 2 bytes. For Baseline and Mid- range PIC devices, the most significant bit is set if the address is that of a data memory- based object; clear if it is that of an object in program memory. For Enhanced Mid-range PIC devices, the state of this bit is reserved: set indicates a program memory address; clear the address of an object in data memory. This encoding enables the pointer to be accessed correctly from assembly code, but will mean that any code that manipulates and dereferences the pointers may be larger and slower than necessary, so it is recommended that pointers are not accessed by assembly code if at all possible. Assembly code may create variables which can be used to hold addresses, if required.Back to top
Why don't my interrupts work after the PIC32 device awakes from sleep/idle mode?
If the PIC32 device enters sleep mode and it is to be woken by the watch-dog timer (WDT) then this FAQ may explain why interrupts are disabled after waking. When the device is awoken from sleep mode using the WDT it triggers a non- maskable interrupt (NMI). These interrupts are hard-wired to vector the PC to the reset vector (i.e. 0xBFC0000 in KESG1 boot flash memory). The compiler generated startup code will test for a NMI and jump to a handling routine. The default routine is simply an ERET instruction which sets the PC to the instruction following the WAIT instruction in the program code which was used to enter sleep mode in the first place. After the NMI has been "serviced" in the above manner the system coprocessor (CP0) STATUS register will have undergone some changes. Firstly the NMI bit in the CP0 STATUS register (STATUS<19>) will be set. This merely indicates that entry through the reset vector was due to an NMI. It should be cleared by the user's code once the NMI has been serviced. Secondly the BEV bit (STATUS<22>) will be set. This bit controls the location of exception vectors and when set all exceptions are vectored to the bootstrap exception vector (i.e. 0xBFC00380 in KESG1 boot flash memory). This bit should also be cleared in the user's code after the NMI has been serviced. Lastly the IE bit (STATUS<0>) will be clear which disables all interrupts. These last two conditions effectively disable interrupts. Here are a couple of suggests to restore interrupts after a waking from sleep mode. 1) After the code which entered sleep mode test the STATUSBack to top
bit and adjust the STATUS register accordingly, e.g.: if (cp0_Status | 0x00080000 cp0_Status ^= 0x00480001; 2) Create an interrupt function to service the NMI and modify the Status register. e.g: void interrupt nmi(void) @ NM_INTERRUPT { cp0_Status ^= 0x00480001; } For more details about NMI's and waking from sleep/idle with the WDT please consult the relevant datasheet and family reference manual.
What optimizations are present in OCG compilers?
The optimizations in OCG compiler can broadly be broadly grouped into: 1) C-level optimizations -- which are performed on the source code before conversion into assembly; and 2) Assembly-level optimizations -- which are performed on the assembly code generated by the compiler Of the C-level optimizations, these can be considered as those that: a) Simplify or change the C expressions; and b) Allocate variables to registers An example of where the code expression may be simplified is this: Consider if the original C code read: a = b + c; but the compiler is able to determine that the variable "c" at this point will always hold the value 5. The code expression is essentially changed so that it reads: a = b + 5; This may result in more efficient code after it is built by the compiler. These sorts of optimizations are inherent in the compilation process and currently cannot be disabled. They may reduce both code and data size. Allocation of variables to registers is done after analysing the code and finding where each variable is used and modified and after determining the cost associated with moving the variable from memory to a register. The compiler is able to determine the exact register usage of each routine, and any unused registers will be considered available for variables. A variable may move from one register to another within a function; it may spend some of its duration in a register and some in its allocated memory location, or its entire duration in a register. Registers can typically be accessed with less code compared to reading from memory. The global optimization level may have some impact on the register allocation. The level of this optimizer (1-9) affects how hard the compiler tries to force variables into registers. Note that even if the global optimizer is disabled, there may still be use of registers for variables in a program. On devices that have few registers (e.g. all 8-bit PIC devices, especially Mid-range PIC devices) the global optimizer has a very limited effect and often it makes little difference to code or data size. As C-level optimizations are performed before debug information is produced, they tend to have less impact on debugging intformation. Note, however, if a variable is located in a register MPLAB IDE may indicate incorrect values in the Watch view. This is due to a limitation in the file format used to pass debug information to the IDE (COFF). Check the assembly list file to see if registers are using in the routine being debugged. Of the assembler optimizations, the actions performed include: i) In-lining of small routines ii) Proceedural abstraction iii) Jump-to-jump type optimizations iV) Peephole optimizations These optimizations can often interfere with debugging in tools such as MPLAB IDE and it may be necessary to disable them, if possible. The assembler optimizations can drastically reduce code size, although typically have little effect on RAM usage.Back to top
Where can I find the old compiler versions?
Previous versions of the compiler are archived at the below mentioned webpage. These versions can be downloaded after logging into the website: http://htsoft.com/downloads/archive.php The latest versions of the compiler can be downloaded from the website http://htsoft.com/Back to top
What version of the Compilers work with Vista/Windows 7?
Vista/Windows 7 support was introduced with the below mentioned versions of the compiler. Compilers released as an upgrade to these versions also support these operating systems. PICC STD 9.60PL2 PICC PRO 9.60PL1 PICC18 STD 9.51PL2 PICC18 PRO 9.61PL1 dsPICC STD 9.60PL2 PICC32 PRO 9.60 e.g. PICC PRO v9.61 & any version above it will work with vista/Windows 7 PICC18 PRO v9.62 & any version above it will work with vista/Windows 7 PICC PRO v9.60 & any version below it will won't work with vista/Windows 7 PICC18 PRO v9.61 & any version below it will won't work with vista/Windows 7Back to top
How do I register my Compiler on microchipdirect.com?
Apart from the activation & installation the compiler you also need to register your product. You need to register your order on microchipdirect.com https://www.microchipdirect.com/loginuser.aspx Register the product using this guide http://www.microchip.com/HPA/#hpa Your MicrochipDirect Account will show the details related to your products. Your Serial number is the Compiler Serial number which should be something like a PICC-xxxxx or PIC18-xxxxx Step by Step guide to Register your Product: Go to microchipdirect.com >> Login / Register >> Services >> Register your Product >> Register New Product >> Fill in these details (Select Product Category / Purchased From/Purchased Date / Serial Number) >> Submit >> Click on the link (Register Support Contract) >>Please Provide Following Information (HPA Serial Number / Development Tool Serial Number) Orders placed via MicrochipDIRECT are fulfilled as follows: * Products are warranted to Microchip datasheet and current errata per applicable Microchip Terms and Conditions of Sale. * Custom product, packaging, or marking is available upon request only * Mixed date code shipments may be sent. * Date codes may be greater than two years. * Microchip Quality System and Customer Service cannot support: * Conformance to specific customer quality specifications * Requests for information or qualification data * Specific environmental information requests * Special terms and conditionsBack to top
What does the error "Could not find space xbytes for auto/param block" mean?
The auto/param block (APB) referred to in the error message, contains all the autos, parameters and temporary storage used by the program. This block is limited in size to no more than one bank and may not even cross bank boundaries. In order to reduce the size you'll need to: 1) Reduce the size or number of function parameters, for instance rather than passing a structure by value pass a pointer to the same instead 2) Change the storage class of some of your local variables to static or make them global 3) Where possible reuse local variables and parameters 4) Use types which are no bigger than what they need to be, for instance the counter variable in for loop over 100 objects need only be as large as a char 5) Reduce the size/ number of secondary data types (arrays etc) in your code. Having too many of these secondary data types would lead to these errors There are a few instances where the PRO compiler will use more temporary storage than the standard. Some of these instances are as: 1) Evaluating function calls whose result is used immediately in evaluating the parameter to another function call. 2) Nested tertiary operators and tertiary operators used to evaluate function parameters 3) Functions requiring reentrancy: these are duplicated by the compiler for each root call graph they appear in. Hence they will have duplicate APBs. The compiler will as much as possible attempt to overlap the APB's of individual functions. Sometimes however these APBs don't always completely overlap with others. These functions sit on the critical path of the program call graph. If you reduce the APB of such functions you'll also reduce the APB of the program. You can identify these functions in the call graph provided in the map file. These functions will be marked with an asterix. For more details about the APB, the call graph and the critical path have a look at the manual.Back to top
How can I create a delay in my program?
If an accurate delay is required, or if there are other tasks that can be performed during the delay, then using a timer to generate an interrupt is the best way to proceed. If these are not an issue, then you can use the compilers in-built delay psuedo-functions: _delay, __delay_ms or __delay_us. These all expand into in-line instructions or a (nested) loop of instructions. The delay amount must be a constant (cannot be a variable) and less than approximately 50659,000. The delay amount is the number of instruction cycles that will be executed by the delay. Earlier versions of the compiler only implemented a two-nested-loop delay and so the maximum delay amount was considerably less. Note that these code sequences may use the CLRWDT instruction.Back to top
Why are SFRs undefined in my assembly code?
If you want to use SFRs in assembly code, you should always include a header files that defines the SFR symbols to use. For PIC10/12/16, useBack to top
for assembly modules, or for in-line assembly. There are also and header files as well. The symbols defined by these file do not have a leading underscore character, so you can use STATUS or PORTA, etc. Other symbols are defined for bits and which specify the register name and bit offset. The symbols are different in the two header files. For aspic.h, TMR0IF is defined as INTCON, 2; in caspic.h TMR0IF_bit defines the same bit. Either can be used directly as the bit operand to an instruction, e.g. bsf TMR0IF. There may or may not be assembly-domain SFR symbols "left over" from the compilation process that you might be able to use in your assembly code, but you should not rely on these being present. Always include the header files mentioned above so that you can guarantee that the symbols you require are defined.
What does a can't find space for a swtext psect mean?
See the FAQ 'What does the error, something like "can't find * words for psect" mean?' for general information about what this error means. A common cause of out-of-space messages that deal with the swtext psect is that the C code contains a switch statement that has too many cases. The swtext psect holds switch statement instructions that are jumps to each case label. The 'computed goto' limitations on base-line/ mid-range devices, demand that this psect wholly fit within a 100h boundary. Special options associated with this psect ensure that these demands are met or an error will be produced.Back to top
How do I install Mac and Linux compilers
The non-Windows compiler installers are in the form of an executable script file that have a ".run" extension, e.g. picc-9.81.7612-osx.run. When running, they install and activate the compiler, prompting you for information as they proceed. The installer must be run from the command line. If you are installing on an Apple Mac computer, first run the Terminal application. The terminal will open in your home directory. Ensure the installer (.run) file is in your home directory, using the FInder to copy it if necessary. Alternatively, you can leave the installer where it is and navigate your way to the enclosing folder in the Terminal application. For example, if the installer was in your Downloads folder, you would type (note that commands and names are case sensitive): cd Downloads Run the installer by typing "./" then the compiler's name. (The command "ls" will list the names of files in the current directory and you can select the name with the mouse cursor, then copy and paste the name using the clipboard.) For example: ./picc-9.81.7612-osx.run The installer will first print the license agreement. Read this, hitting the "space" key until all the agreement has been seen and you see the prompt "Do you accept the terms of the licence agreement?" Type "y" to accept, or "n" to quit. The installer will ask for the directory in which to install the compiler. The default directory path is printed in square brackets, but you will probably want to change this. Type the new path using forward slashes to separate directories; do not use the "~" character. You may wish to install it in the Applications folder, or somewhere in your own account if you are the only account holder to use it. Below are two examples, the second assuming the account name is "john". /Applications/hitech/picc/9.81 /Users/john/hitech/picc/9.81 The directories will be created if they do not exist. It is recommended that you include the compiler type and version number in the directory structure, particularly if you plan to update to later versions of the compiler. You can have more than one compiler installed at the same time. The installer will next prompt you for your serial number. Enter only the digits of your serial number. If you do not have a serial number and wish to evaluate the compiler, enter "demo". If you want to permanently run the compiler is the free Lite mode, enter "lite". Activation should take place automatically over the Internet and you should see the installer confirm that installation and activation was successful. The compiler can be invoked by running the compiler driver (either "picc" or "picc18" for the PIC10/12/16 or PIC18 compilers, respectively). For example, if you had installed the compiler in the Applications folder, as above, you can run the compiler with the commands: /Applications/hitech/picc/9.81/bin/picc --ver which will print the version number and quit; or: /Applications/hitech/picc/9.81/bin/picc --help which will print help for the driver options, then quit; or /Applications/hitech/picc/9.81/bin/picc --chip=16f877 main.c isr.c which will build and link the two source files indicated for the PIC16F877 device. The last example assumes that you were in the same directory as the source files when you issued the command. The procedure is identical for Linux installations, although the directory paths will be different.Back to top