Jump to content
Sign in to follow this  
ppulle

Limits On Switch And Or Function Sizes

Recommended Posts

Hi,

 

I'm using Sourceboost 6.87 on a PIC18F4550 target.

 

I've just started getting strange behavior that looks like a stack overflow though my stack depth is less than 8 for the functions being called. That is functions are stopping for no apparent reason.

 

I was wondering if there is a limit on the number of case statements in a switch function, or if there is a limit on function sizes. This seems the likely culprit as the problems started occuring just after adding new functionality.

 

In my case I have a command handler which is now 2188 bytes long with 43 case statements..... is this too much??

 

Phil

Share this post


Link to post
Share on other sites

ppulle,

I'm using Sourceboost 6.87 on a PIC18F4550 target.

 

I've just started getting strange behavior that looks like a stack overflow though my stack depth is less than 8 for the functions being called. That is functions are stopping for no apparent reason.

 

I was wondering if there is a limit on the number of case statements in a switch function, or if there is a limit on function sizes. This seems the likely culprit as the problems started occuring just after adding new functionality.

 

In my case I have a command handler which is now 2188 bytes long with 43 case statements..... is this too much??

There is no intended limit.

Branch instructions have a limit of -1024 and +1023 16 bit locations (or twice that number of bytes), might be worth checking the .asm for longest branches. All should be in range, if the destination address is too far for branch, goto is used.

 

Regards

Dave

Share this post


Link to post
Share on other sites

Hi Dave,

Thanks for the headsup. It saved me a fair bit of time unnecessarily rearranging my switch statement to see if I could shorten it.

 

I found the problem, though it's a little off topic (nothing to do with switch statements or function sizes), I thought I might explain it here as the process I went through might help others.

 

I had some hardware which basically logged accelerometer and other data to a memory chip. It has a USB connection to download these logs and control bits and pieces. I use a library of USB functions to do the USB connection, that is a compiled library linked in, not some code in C compiled every time.

 

I added an i2c peripheral, a magnetometer, and started logging this data. For various reasons I was using software i2c emulation. Initial tests were OK.

 

Next I added some more functionality and I found that when I was doing a USB conversation things would stuff up. I originally thought this was in the case statement as this was where the new functions were added...hence the topic question.

 

Luckily I keep a weekly archive of my code (hint number one for new programmers). I went back to the old stuff before the new code was added and looked at the '.asm' file. I copied the EQU statements where memory was allocated to a spreadsheet and sorted it. I did the same for the new code. Flicking between spreadsheet pages let me see what was different.

 

I noticed that there was a data structure in the old working code that was allocated into the first page of memory (fyi the USB configuration structure). With the new code added it had been allocated out of that page.

 

Looking a bit further it appeared that the #defines for the i2c software emulation were by default being allocated right in the middle of my data structure, so naturally the linker moved the data structure around it....fair enough

 

Unfortunately the code that used the data structure must have been expecting this structure to be in the first page, when it was moved it behaved erratically.

 

I simply redefined the i2c software emulation #defines out of the data structures path. Now the linker puts everything where the library code expects it to be.

 

So some lessons:

- when writing code for compiled libraries be aware that the linker may move data structures around. If your code is optimised for one page or another, there will be trouble.

- I still might have some problems if the linker moves data around....but at least I know to check for it now.

- Check your asm file if things go weird.

 

Some questions:

- how should code for a compiled library be written to ensure that it is impervious to data structures being moved about by the linker? I checked that there was no assembly code, so I presume the compiler and linker at library creation time put the data access optimisations in (I used linker -O1 -rb 0x0800, no optimisations on the compiler command line).

- What optimisations are not recommended for compiled libraries? Is this a bug?

 

Some suggestions:

- can we get an IDE function to provide a sorted memory map on the codebar, preferably showing whats defined, how big it is and where. Even better showing what functions are using the memory allocations. Using a spreadsheet was OK.....but a little fiddly.

 

Phil

 

As an afterword as I was moving the #defines for the i2c driver about I noticed that one build overfilled by 400 locations, yet another build left me with 206 bytes free.....I only had to move it 8 places to get it good......but just goes to show how memory allocation can affect the compiled code size, presumably due to extra page whacking being necessary or not necessary. I wonder if there is scope for improvement in the linker/compiler combination there?

Share this post


Link to post
Share on other sites

ppulle,

Unfortunately the code that used the data structure must have been expecting this structure to be in the first page, when it was moved it behaved erratically.
If it is C code that accesses the data structure, and there is no manually added code added to do bank switching (this is not a good thing to be doing) then the linker should add appropriate bank switching.

 

If you can supply a 'bad' project, with some details of where the problem is then we can take a look.

 

Regards

Dave

Share this post


Link to post
Share on other sites

Hi Dave,

 

I've had a close look at the asm code, and it appears that in the 'bad' project, the linker is adding all the appropriate bank switch statements to access the reshuffled variables (ie movb instructions), as well as using the different op codes for page zero vs page something else accesses. It looks like this is being done even for the few asm{ } constructs as well. In other words it appears the linker and compiler are doing their proper function.

 

It's probable then that maybe I've looped somewhere and done a buffer overrun or equivalent. In the 'good' project the buffer may be overrun into something harmless like a temporary variable of a function that doesn't interfere with things and in the 'bad' project the reshuffling of variables puts the overrun into something critical. Without an ICD I can't tell.....unfortunately my comms is the thing going wrong so debug statements won't help either. Looks like I'll have to manually check all my looping/array operations..yuk. Can you make a suggestion on any free tools to help?

 

I'll give a later version of the compiler a go, and see if that helps. Only problem is I won't know what was causing the problem, which is never a good thing.

 

I was wondering if you could answer whether there is an optimisation option in the linker that re-shuffles variable allocation to minimise bank switching. The compiles I've been doing that manually shuffle things have been showing quite big differences (hundreds of bytes, important when you're close to the limit) in the code size due to bank switching either being necessary or not necessary, or being inserted in code fragments when multiple variables across various pages are being used.

 

Phil

Share this post


Link to post
Share on other sites
Hi Dave,

 

I've had a close look at the asm code, and it appears that in the 'bad' project, the linker is adding all the appropriate bank switch statements to access the reshuffled variables (ie movb instructions), as well as using the different op codes for page zero vs page something else accesses. It looks like this is being done even for the few asm{ } constructs as well. In other words it appears the linker and compiler are doing their proper function.

 

It's probable then that maybe I've looped somewhere and done a buffer overrun or equivalent. In the 'good' project the buffer may be overrun into something harmless like a temporary variable of a function that doesn't interfere with things and in the 'bad' project the reshuffling of variables puts the overrun into something critical. Without an ICD I can't tell.....unfortunately my comms is the thing going wrong so debug statements won't help either. Looks like I'll have to manually check all my looping/array operations..yuk. Can you make a suggestion on any free tools to help?

 

I'll give a later version of the compiler a go, and see if that helps. Only problem is I won't know what was causing the problem, which is never a good thing.

 

I was wondering if you could answer whether there is an optimisation option in the linker that re-shuffles variable allocation to minimise bank switching. The compiles I've been doing that manually shuffle things have been showing quite big differences (hundreds of bytes, important when you're close to the limit) in the code size due to bank switching either being necessary or not necessary, or being inserted in code fragments when multiple variables across various pages are being used.

 

Phil

Share this post


Link to post
Share on other sites

Hi Dave,

Sorry if the below sounds confused and incoherent...but I'm a little lost at the moment.

 

I updated to version 6.96 but with the same result.

 

Next I looked closely at the i2c_driver.h file, and noticed that some of the variable definitions were short instead of char and the #defines pointing to the variable allocations were out of order (I think this has been noted in another post). I fixed these up and the code works properly.

 

This is a bit puzzling, as even if I don't call the i2c functions, the code fails (if I set the i2c_driver.h back to the way it was that is).

 

I can only think that the changes (short to int) in the i2c_driver.h file just does a rearrangement of the memory allocation to a harmless format as I noted below. Without the changes the memory map may have done something bad.

 

In the asm file I've noticed that there are a lot of CompTempVarxx variables reusing memory allocation (as well as function arguments and return values). No doubt the linker makes sure that nothing is shared when it shouldn't be for a normal call tree. I've also go no doubt the linker makes sure call trees starting from an interrupt call don't inappropriately share temporary variables.

 

Could there be a issue with the fact I'm calling a library function inside my interrupt which may be confusing the linker with regards to temporary variable allocation and sharing?

Just to add to the question above, the library function in the interrupt call tree also uses a trick to emulate a function pointer call. This is my current suspect. The library code was written long ago before function pointers were supported by Sourceboost, however does the current version deal with any temporary variables etc called from a function pointer in an interrupt function?

 

Here is the function pointer call emulation code:

void __osEmulateFunctionPtr(pFunc pFPtr)
{
 byte Savedintcon=intcon;
 intcon.GIEH=0;
 intcon.GIEL=0;  

 asm { 
// push a dummy entry onto the hardware stack
push  
// copy the three bytes which define the address
movff _pFPtr,_tosl
movff _pFPtr+1,_tosh
movff _pFPtr+2,_tosu
 }; 

 intcon=Savedintcon;
 // now enter the routine who's address is at (TOS)
 return;
}

Obviously this would confuse any compiler or linker, the question above is to clarify whether the new version of sourceboost properly makes sure the temp vars of a function pointer call are not shared, particularly when done in an interrupt function.

 

FYI the reason for this was the library was adapted from Microchips CDC code which used function pointer arrays to call the appropriate USB service handlers if you had a USB connection with multiple protocols. Fortunately I'm only using one, so I can make a direct call and bypass the function pointer call, probably save some code space as well.

 

I'll update this thread when I've re-written the library to use the newly support function pointer calls or have simply bypassed the use of any function pointers.

 

Phil

 

PS: As an aside, is there an existing option, or can an option be added to a new release, to show the variables used (including temp variables) for each function in the map file?

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...