mP PRO for PIC quirks and errors

Compiler and system libraries

A lot has been done over last few years to perfect the command line compiler, but there are still few quirks there, and some system libs certainly need modifications. Problems already fixed in versions before 6.40 are not presented here. Note that version 6.50 has not brought changes to compiler, version 6.61 solved only one problem (introduced in v 6.40), and version 6.6.2 introduced new error. Fortunately, first time in last two years, version 7.00 significantly reduced the number of errors. Unfortunately, version 7.2 introduced unnerving IDE quirks and newly added processors of K42 and K83 series are not properly implemented.

- errors in K42 and K83 processor series implementation

Most critical consequences arise from the fact that compiler has hard-coded location of the SFRs' part of access bank as top part of BANK15 while K42 and K83 families have this part of memory located at top part of bank 63. In effect, even simple byte division may produce uncontrollable program behaviour.Some SFRs used in libraries, like FSRx registers, are treated even more badly as linker leaves them located where they were for processor for which the library was compiled, not where they're located in new processors (it's not fully predictable, as it depends on how the library source code used the FSRs). For this reason, large part of String and Conversions libraries is useless for K42 and K83 processors, as well as many of routines that use indirect addressing in other libraries. Additionally, there is no ICD support for these processors and software simulator doesn't fully work for them. And finally, new features of these processors that could be very useful, like vectored interrupts, are not implemented.

common quirks and errors (present in earlier and current compiler version)

- quirks in fixed and floating-point math libraries are enumerated in respective parts of replacement libraries description.

 Additionally, in case of PIC16 and enhanced family, 8-bit signed multiplication routine produces results depending on order of operands:
   -1 * -128 = 0
   -128 * -1 = -128
    1 * -128 = 0
    -128 * 1 = -128
- results of runtime calculations may differ from compile-time ones

The reason is two-fold: different evaluation rules are applied in both cases and there are some imperfections in math libraries.
Differences for integral types due to evaluation rules were removed in v7.00, but those for floating-point arithmetic are still present, like in
     y:=2E-38/x*10E5;
Here, for x=2, result will be zero at runtime but a nonzero (accurate) value at compilation time. Again, the difference is due to partial result being outside of range. Compile calculations do not fully mimic the Microchip floating-point range.

Some special cases are evaluated differently due to math libraries peculiarities, like modulo operations with negative integers:
     Byte2 := Byte1 mod -1;
If Byte1 is negative, result will be negative at compile-time, but positive at runtime.

Other special cases giving wrong results at runtime:
   - division of short by 0 gives -128, whatever the dividend's sign
   - division of short -128 by -1 gives -128
   - division of positive odd and negative even short numbers by -128 gives 1, not 0 (fixed in v5.00)
   - division of integer or longint number by 0 gives positive value,
     whatever the dividend's sign
   - error in 32-bit signed division routine leads to remainders smaller by one in
     magnitude if dividend equals minimum longint value which leads to incorrect results
     of modulo operations

BTW, the example of a floating-point expression presented above will, for x=0, result in a small value instead of infinity (represented by maximum positive value allowed in Microchip format). It may also be a problem in some cases that division by zero of negative argument gives positive value.

- mixing bit and boolean logic in one expression may lead to errors

There are too many cases when it may go wrong, so the general conclusion is - don't mix these two types of logic. In fact, even using boolean logic with bits may lead to errors, like in
  if not((bit1=1) and bool_var) then  // always evaluates true if bit1=1
because compiler tries to use both bit logic and boolean logic at the same time (in spite of bit1=1 being boolean expression). Even pure bit logic not always works, like in
  if not PORTB.i then  // always evaluates true
(i is a variable here).

If one needs to mix bit and boolean logic then my System replacement library contains function bool that may be applied to bit part of expression fixing the problem, like
  if not(bool(bit1) and bool_var) then

  if not bool(PORTB.i) then

- while evaluating in main a byte holding sbits declared in a unit optimizer may overlook changes applied to single bits

An example will surely explain this one better than mere words. Lets assume that in a certain unit a holding byte, bvar, is declared together with some of its bits defined as sbits
var
  bvar: byte;
  bvar1: sbit at bvar.1;
  bvar4: sbit at bvar.4;
Now, if one wants to use these in main, for example by clearing the holding byte and then manipulating the sbits
  bvar:=0;
  bvar1:=1;
  bvar4:=1;
  PORTB:=bvar;
one will be unpleasantly surprised by PORTB being cleared. The problem is in optimizer seeing no connection between the holding byte and sbits. Simplest way to avoid it is to declare the holding byte volatile thus preventing optimizer from assuming any previous state of this variable.

- applying sfr modifier to a variable declared at absolute address in GPR space makes the linker believe it's indeed in SFR space, so it's real space is freed and may be taken by another variable

- FSR registers are not saved in ISR unless used in assembly

Not much explaining is required here - if one used indirect addressing in Pascal code (like in next example) both in main code and ISR(s), one may have to save & restore used FSR registers in ISR(s).

- modifying variables with the use of FSR registers may confuse optimizer

Again, an example should make the problem demonstrated clearly:
    w2:=0;
    //asm nop end;
    FSR0Ptr:=@w2;
    INDF0:=5;
    w2:=w2+1;	  // w2=1 not 6
Without the commented line, optimizer will overlook changes done to w2 through indirect addressing and assume old value still valid. The short assembly insert blocks optimizer leading to proper evaluation of w2. Same may be achieved by declaring w2 volatile.

- including units in Project Manager leads to complete disregard of any unit dependencies introduced with uses clauses (any unit included in PM is connected directly to main - like there was a uses clause)

- placing local vars in rx space is ineffective as they're not reused; when used in libs, may cause linker error for lack of space; moreover, rx-ed local vars have global scope!

- FSR registers are used extensively by compiler

For example, single statement in following procedure uses all three FSRs of a PIC18 processor
       procedure test(var bb,bc:byte);
        begin
         bb:=bc+bb;
        End;{test}

- empty 'badram' declaration in *.mcl file causes compiler to assume that 0 is bad RAM location.
Happily enough it does not happen frequently - few processors, like PIC18F87K22, have no "bad RAM" locations. Still, compilers behaviour causes unnecessary problems with more advanced coding and one has to modify the mcl file declaring some dummy 'badram' location.

- System and Math library for enhanced processors series contain errors that may lead to overwriting of unrelated parts of RAM.
This may happen when handling large (>255 bytes) memory chunks or while performing math division when divisor happens to be zero.

- unlike for P16 and P18 processors, for enhanced family processors a call to assembly routine, even within single assembly block, causes compiler error ('Invalid ASM instruction').

- with P18FxxK40 processors, use of any of EEPROM library routines requires setting the NVMREG1 bit of NVMCON1 register afterwards - otherwise some libraries (ones requiring accessing flash directly) may no longer function (fixed in v7.20).

- some libraries (like GLCD) do not function properly with P18FxxK40 processors due to different distribution of TRISx and PORTx registers in these processors (fixed in v7.20)
(libraries controlling port pins are based on assumption of identical relative spacing of these registers in all PIC18 processors).

- division by constant being a power of 2 is optimized to right shifting which produces errors for negative dividends for all signed types (short, integer, longint) when dividend is not a multiple of divisor (quick fix applied in v7.00 at the cost of optimization)

Here's an example:
 var x,y:short;

begin
 x:=-3;
 asm nop end;   // prevents compile time calculations (which are done correctly)
                // x=-3  x=-99  x=-1
 y:=x/2;        // y=-2  y=-50  y=-1 wrong!
 y:=-(-x/2);    // y=-1  y=-49  y=0  fine (as a positive number is being shifted here)
This error is due to optimizing division by powers of 2 (replacing division with right shifting) while overlooking the fact that negative numbers require additional attention while shifting. Same is true when compiler replaces division by 256 by moving whole bytes (for example, runtime calculations produce -513/256 = -3 !).
The following is no longer true in v 6.61 (except for mC PRO ;)):
In enhanced PIC16 family this error is also 'enhanced' when dividing by 2. In the example above one obtains -3/2=126 and -99/2=78 !.


- negation of HiWord, LoWord produces wrong results when argument is zero or 00FF (fixed in v7.00)

This happens only at compile time when constant values are assigned to variables, like
 var wval1,wval2:word;
 const dwconst1:dword=0x00FF00FF;
       dwconst2:dword=0x00000000;

begin
 wval1:=not HiWord(dwconst1); // error - should be 0xFF00, not0x0000
 wval1:=not LoWord(dwconst1); // error - should be 0xFF00, not0x0000

 wval1:=not HiWord(dwconst2); // error - should be 0xFFFF, not0x00FF
 wval1:=not LoWord(dwconst2); // error - should be 0xFFFF, not0x00FF

- in some cases optimizer may fail to notice that part of a variable was changed using Hi, Lo, Higher, or Highest operators (fixed in v7.00).

Problem shows up when one rearranges bytes in multi-byte variable storing intermediate results in a temporary variable.

- while adding bank switching instruction linker may fail to check preceding instruction - which may be a bit-testing one that skips next instruction, like BTFSC (fixed in v7.00)

This happens when optimizer replaces more complicated expression with conditional assembly instructions.
Here's an example:
var i: byte; absolute 0x100;
    j: byte; absolute 0x200;

begin

 j:=(i and $02) shr 1;
The above expression effectively reduces to j:=i.1 so compiler produces code that tests single bit but, while linking, fails to properly adapt the code when involved variables are located in different banks. In effect, not only the j variable is always cleared but a corresponding place in another memory bank is modified. Resulting assembly demonstrates the issue
      	MOVLB       2
      	CLRF        _j, 1
      	MOVLB       1
      	BTFSC       _i, 1, 1
      	MOVLB       2         <--- may be skipped by preceding instruction
      	INCF        _j, 1, 1

- when one appends constant string to string with '+' operator compiler forgets to add terminator (fixed in v7.00)

An example:
   var str1,str2: string[10]
	const cstr='def';

	str1:='abc';
	str2:='12345678'
	str2:=str1+cstr; // str2='abcdef78'

- letting compiler extend type in expression may lead to incorrect results (PIC16 and enhanced only) (fixed in v7.00)

Care should be taken in arithmetic expressions where right-side calculations require higher type that the result (left-side variable type). In such case explicitly typecasting the right-side expression to left-side type helps. For example
 dim value as longword
 dim clk as word
 dim m as word

 value = 19200
 asm nop end asm ' (prevents compile-time calculations)
 m = ((Clock_MHz * 1000000) div value)-1      ' m = 159
 asm nop end asm
 m = word((Clock_MHz * 1000000) div value)-1  ' m = 415

- optimizer may skip code that was supposed to replace division by power of 2 by right-shifting. It happens only when unsigned variable was assigned to a signed one and divisor equals 8 or 16 (fixed in v6.62)

Here's an example:
var int_v: integer;
    word_v: word;
begin
  uint_v := 1024;
  asm nop end;            // just to avoid following calculations performed at compile-time
  int_v := uint_v;        // int_v = 1024
  int_v := int_v/8;       // no code is generated for this line!
  int_v := int_v-100;     // int_v = 924!
end.
Effects may be unpredictable if the variables are of different types. In such case residual code that was supposed to move result of shifting to left-hand variable may produce incidental values.

errors introduced in v6.62 - fixed in v6.63

- functions Hi, Higher, Highest, and HiWord may not function properly at compile time

For example
var dwval: dword;
    wval1,wval2: word;
    bv1,bv2,bv3,bv4: byte;

begin
 dwval:=0x12345678;
 wval1:=LoWord(dwval); // wval1=0x5678
 wval2:=HiWord(dwval); // wval2=0x5678  !!!
 bv1:=Lo(dwval);       // bv1=0x78
 bv2:=Hi(dwval);       // bv2=0x78 !!!
 bv3:=Higher(dwval);   // bv3=0x78 !!!
 bv4:=Highest(dwval);  // bv4=0x78 !!!
end.

Processor definition files - vast improvement in v7.00

Before any serious work it is advisable to check processor definition files (placed in Def folder of compiler installation directory) against respective processor datasheet. Unfortunately, these files are not adequately verified and - especially newer ones - may contain errors. Mistakes in *.pas (*.bas) definition files are rare - sometimes declarations of some obscure registers or bits are missing, only occasionally some other mistake appears. Mostly, mistakes are made in *.mlk files which are written in xml format and contain definitions of processor data and program memory structure, interrupt vectors, configuration words, and list of libraries applicable for specific processor.

Besides the old problem with configuration words (written words may differ from read ones due to improper coding of unused bits) and sometimes cryptic naming of specific bits, there may happen (fortunately rare) mistakes in bits representation.

Many mistakes that may have been found in 'bad ram' declarations (unused SFR locations) of newer processors of the enhanced family were removed with release of v7.00. Same about missing 'Mapped Memory' declarations.

Unfortunately, definition files for new processor series, the PIC18FxxK40, do not take into account that compiler adds 0xF00000 to EEPROM address parameter thus overshooting the real address in K40 processors. Solution for the moment is to compensate in respective *.mlk definition file for the automatically added offset by declaring negative EEPROM addresses:
   <EEPROM>
      <MIN_ADDR>0xFF410000</MIN_ADDR>
      <MAX_ADDR>0xFF4103FF</MAX_ADDR>
   </EEPROM>
As this was not resolved before mE included newer K-series processors (like K42 or K83) with similar memory organization, the above applies to these processors, as well.


Code Editor

The following has been collected here just to save others trouble of discovering the same issues or reporting them to mE. mE is aware of the problems listed and certainly will eliminate them with time as already happened with some of the issues (well, maybe not as this was written years ago and the list doesn't change).

- tabs are always replaced by spaces - whatever the settings
- cursor position while marking text close to gutter may be not recognized - actual start of marking may be the last cursor position
- changes to order of files in Edit window are not saved
- many of Edit Options do not work
- while marking text close to right margin, cursor tends to run away to far right


TopTop        MainHome        Copyright © 2011-2018 by janni