CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Problems with large struct.

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
haxan7



Joined: 27 Jul 2013
Posts: 79

View user's profile Send private message

Problems with large struct.
PostPosted: Tue Oct 13, 2015 9:34 am     Reply with quote

I think it is a compiler bug, I need help to go around it.
When I try to initialize the struct, the value assigned to a variable (any possibly others as well) is garbled up.

uController: PIC18F46K22
Compiler: 5.047 and 5.049


The is code is too large to post, here is a excerpt that causes problems:

Code:


#define DBGLU(x) fprintf(DEBUG, "%s=%Lu\r\n",#x,x)

typedef void (*callbackFunction)(void);


int _debounceTicks = 50; // number of ticks for debounce times.
typedef struct{
   uint16_t _pin;
   
   // These variables will hold functions acting as event source.
   callbackFunction _clickFunc;
   callbackFunction _doubleClickFunc;
   callbackFunction _pressFunc;
   callbackFunction _longPressStartFunc;
   callbackFunction _longPressStopFunc;
   callbackFunction _duringLongPressFunc;
   
   uint16_t _clickTicks; // number of ticks that have to pass by before a click is detected
   uint16_t _pressTicks; // number of ticks that have to pass by before a long button press is detected

   bool _buttonReleased; // Logic level when button is NOT pressed
   bool _buttonPressed; // Logic level when button is pressed

   bool _isLongPressed;
   
   // These variables that hold information across the upcoming tick calls.
   // They are initialized once on program start and are updated every time the tick function is called.
   uint8_t _state;
   uint32_t _startTime;  // will be set in state 1
}Button;

void Button_init(Button* b, uint16_t pin, bool activeLow=1){
  b->_pin = pin;

  b->_clickTicks = 2000;        // number of millisec that have to pass by before a click is detected.
  b->_pressTicks = 1000;       // number of millisec that have to pass by before a long button press is detected.
 
  DBGLU(b->_clickTicks);

  b->_state = 0; // starting with state 0: waiting for button to be pressed
  b->_isLongPressed = false;  // Keep track of long press state

  if (activeLow) {
    // button connects ground to the pin when pressed.
    b->_buttonReleased = 1; // notPressed
    b->_buttonPressed = 0;

  } else {
    // button connects VCC to the pin when pressed.
    b->_buttonReleased = 0;
    b->_buttonPressed = 1;
  } // if


   b->_doubleClickFunc = NULL;
   b->_pressFunc = NULL;
   b->_longPressStartFunc = NULL;
   b->_longPressStopFunc = NULL;
   b->_duringLongPressFunc = NULL;
   
  DBGLU(b->_clickTicks);
}


output to the serial console is
Code:

b->_clickTicks=2000
b->_clickTicks=0
Ttelmah



Joined: 11 Mar 2010
Posts: 19249

View user's profile Send private message

PostPosted: Tue Oct 13, 2015 10:38 am     Reply with quote

Try storing an int16, instead of a function address, and explicitly cast this to a function address when required.

Look at this thread:
<http://www.ccsinfo.com/forum/viewtopic.php?t=49499>

CCS persistently sometimes has problems with function pointers.

Then separately, simplify the alignment for the compiler. Declare your single bit values last, and use the C standard bitfield, rather than bool. Again CCS sometimes does nasty things with bool values.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Oct 13, 2015 11:04 am     Reply with quote

Oops, I was typing in my huge post but Ttelmah already answered it.
Oh well.

The problem appears to be this:

1. The compiler allocates 2 bytes of RAM in the structure for each
callback function declaration. That's correct.
In the 18F46K22, there are 64 kbytes of flash memory. The address
range is 0 to 0xFFFF, which requires 16-bits to hold the address of a
function.
So the offset of each function address in the structure is 2 bytes higher
than the previous entry. That's all reasonable.

2. When you initialize all the callback function addresses to NULL (or it
could be to any address), the compiler writes to 4 sequential bytes.
That's not correct. By writing to 4 bytes instead of 2, it's stepping on
the next callback function address in the structure. That's why you
get 0000 as the 2nd result.

For example, suppose we edit your code to set the last entry below
to 0x1234 instead of NULL:
Code:
b->_doubleClickFunc = NULL;
b->_pressFunc = NULL;
b->_longPressStartFunc = NULL;
b->_longPressStopFunc = NULL;
b->_duringLongPressFunc = 0x1234;


Now look at the .LST file code for the last 3 lines above:

First you can see the structure offset of each entry at the start of each
block. It goes 8, 0A, 0C, which is 2 bytes apart, because a function
address only takes 16 bits in this PIC.
Code:

....................    b->_longPressStartFunc = NULL; 
00194:  MOVLW  08
00196:  ADDWF  b,W
00198:  MOVWF  FSR0L
0019A:  MOVLW  00
0019C:  ADDWFC b+1,W
0019E:  MOVWF  FSR0H
001A0:  CLRF   INDF0
001A2:  CLRF   PREINC0
001A4:  CLRF   PREINC0
001A6:  CLRF   PREINC0
....................    b->_longPressStopFunc = NULL; 
001A8:  MOVLW  0A
001AA:  ADDWF  b,W
001AC:  MOVWF  FSR0L
001AE:  MOVLW  00
001B0:  ADDWFC b+1,W
001B2:  MOVWF  FSR0H
001B4:  CLRF   INDF0
001B6:  CLRF   PREINC0
001B8:  CLRF   PREINC0
001BA:  CLRF   PREINC0
.................... 


Now let's look at the final section. It loads 34 12 (in Intel lo-hi format)
into the structure element, but then it continues on and clears two
additional bytes. Why ? This is some kind of bug. Note that all callback
address entries are getting this treatment. So they're all stepping on
each other. Note that this problem only occurs with function addresses.
Code:

....................    b->_duringLongPressFunc = 0x1234; 
001BC:  MOVLW  0C
001BE:  ADDWF  b,W
001C0:  MOVWF  FSR0L
001C2:  MOVLW  00
001C4:  ADDWFC b+1,W
001C6:  MOVWF  FSR0H
001C8:  MOVLW  34
001CA:  MOVWF  INDF0
001CC:  MOVLW  12
001CE:  MOVWF  PREINC0
001D0:  CLRF   PREINC0    // *** Steps on the next 2 bytes *** BUG !
001D2:  CLRF   PREINC0
.................... 


A simple brute force workaround is to add a dummy int16 variable after
each callback function address declaration. Then the code will just be
stepping on a dummy variable, and who cares what happens to that ?
This work-around should at least allow you to continue development
until somebody can come up with an explanation, or a better workaround.
CCS should be asked about why it's doing this.
I tested this with CCS vs. 5.050 in MPLAB vs. 8.92 simulator.
Quote:

typedef struct{
uint16_t _pin;

// These variables will hold functions acting as event source.
callbackFunction _clickFunc;
int16 dummy0;
callbackFunction _doubleClickFunc;
int16 dummy1;
callbackFunction _pressFunc;
int16 dummy2;
callbackFunction _longPressStartFunc;
int16 dummy3;
callbackFunction _longPressStopFunc;
int16 dummy4;
callbackFunction _duringLongPressFunc;
int16 dummy5;



Here's the test program I made to work on this. You actually should
have made this for me.
Code:
#include <18F46K22.h>
#fuses INTRC_IO, NOWDT
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS, stream=DEBUG)

#include <stdint.h>

#define bool int1
#define NULL (int16)0

#define DBGLU(x) fprintf(DEBUG, "%s=%Lx\r\n",#x,x) // *** Was %lu ***

typedef void (*callbackFunction)(void);


int _debounceTicks = 50; // number of ticks for debounce times.

typedef struct{
   uint16_t _pin;
   
   // These variables will hold functions acting as event source.
   callbackFunction _clickFunc;
   int16 dummy0;
   callbackFunction _doubleClickFunc;
   int16 dummy1;
   callbackFunction _pressFunc;
   int16 dummy2;
   callbackFunction _longPressStartFunc;
   int16 dummy3;
   callbackFunction _longPressStopFunc;
   int16 dummy4;
   callbackFunction _duringLongPressFunc;
   int16 dummy5;


   uint16_t _clickTicks; // number of ticks that have to pass by before a click is detected
   uint16_t _pressTicks; // number of ticks that have to pass by before a long button press is detected

   bool _buttonReleased; // Logic level when button is NOT pressed
   bool _buttonPressed; // Logic level when button is pressed

   bool _isLongPressed;

   
   // These variables that hold information across the upcoming tick calls.
   // They are initialized once on program start and are updated every time the tick function is called.
   uint8_t _state;
   uint32_t _startTime;  // will be set in state 1
}Button;


void Button_init(Button* b, uint16_t pin, bool activeLow=1){
  b->_pin = pin;

  b->_clickTicks = 2000;        // number of millisec that have to pass by before a click is detected.
  b->_pressTicks = 1000;       // number of millisec that have to pass by before a long button press is detected.
 
  DBGLU(b->_clickTicks);

 
  b->_state = 0; // starting with state 0: waiting for button to be pressed
  b->_isLongPressed = false;  // Keep track of long press state


  if (activeLow) {
    // button connects ground to the pin when pressed.
    b->_buttonReleased = 1; // notPressed
    b->_buttonPressed = 0;

  } else {
    // button connects VCC to the pin when pressed.
    b->_buttonReleased = 0;
    b->_buttonPressed = 1;
  } // if



   b->_doubleClickFunc = NULL;
   b->_pressFunc = NULL;
   b->_longPressStartFunc = NULL;
   b->_longPressStopFunc = NULL;

   b->_duringLongPressFunc = 0x1234;

   
   DBGLU(b->_clickTicks);
}


//=====================================
void main()
{
Button b;
uint16_t pin;
bool activeLow;

activelow=1;
pin = PIN_B0;

Button_init(&b,  pin, activeLow);

while(TRUE);
}
haxan7



Joined: 27 Jul 2013
Posts: 79

View user's profile Send private message

PostPosted: Tue Oct 13, 2015 11:43 am     Reply with quote

Spot on. Thanks a lot. My program definitely works now.
Apologies for any inconvenience caused by me by not posting a compilable program.
Ttelmah



Joined: 11 Mar 2010
Posts: 19249

View user's profile Send private message

PostPosted: Tue Oct 13, 2015 2:07 pm     Reply with quote

It is interesting, since it relates to that old post (long time ago, so the problem has been around a while). PCM_programmer has nailed it down further. It appears that when a function pointer is inside a structure, CCS is not correctly remembering the size, and treating them as if they were the largest size (that applies to PCD compilers and some of the larger PIC18's), where they have to be > 16bit. Yet elsewhere it correctly adjusts the size for the chip involved. Hence my solution of using numeric values at the size needed for the processor works, or leaving the extra space as PCM_programmer does also works.

There is a (relatively) elegant work-round that also works and doesn't waste the space. I redeclared the typedef as a union between a function pointer, and an int16 (called ptr, and val), and then just write the NULL to

b->_doubleClickFunc.val = NULL;

Then when I want to use the function pointer, use

b->_doubleClickFunc.ptr

This seems to be working OK. Smile
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Oct 19, 2015 8:53 am     Reply with quote

I emailed a bug report to CCS about this thread. Here is their reply:
Quote:

Thanks for the problem report. This will be fixed in 5.051 due out early this week.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group