|
|
View previous topic :: View next topic |
Author |
Message |
Guest123 Guest
|
Strange behavior of function return value |
Posted: Wed Oct 15, 2008 4:47 pm |
|
|
Hello
I encountered a very strange behavior of a function return value. For my overall program structure I coded a Timer Module which allows me to generate programmable timeouts. For this reason i have defined the following structure which holds all information for one timer.
Code: |
typedef struct timref_t
{
int1 signal_flag; ///< Holds the timer flag
unsigned int16 cnt; ///< Current counter value for the timer
unsigned int16 cmp; ///< Compare value for the timer
};
|
In order to have an encapsulated module I have coded a function which returns me the value of the signal_flag:
Code: |
int1 getTimeout(struct timref_t* ref)
{
return ref->signal_flag;
}
|
In the main function I use the Timer module like this:
Code: |
void main(void)
{
struct timref_t myTimRef;
setTimer(100, &myTimRef);
while(1)
{
if(getTimeout(&myTimRef))
{
// some code
}
// some code
}
}
|
However the problem is, that the if condition will sporadically be true even if the signal_flag of myTimRef is still 0. I used an ICD debugger to find out what is wrong. However I found out that it sometimes returns the correct result and sometimes not. It seems like the pointer (struct timref_t* ref) in the getTimeout function is corrupted sometimes but I couldn't reconstruct when this happens and due to what reason it happens. Has anybody encountered a problem like this before (or can someone see the stupid fault I made :-))?
Compiler Version PCH V4.079
Compiling for a PIC18F2620
I hope that anyone can help me I'm really annoyed by this misbehavior...
Thanks in advance |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Oct 15, 2008 4:52 pm |
|
|
The first thing I would do to test this, is to get rid of the 'int1'.
Change it to 'int8'. |
|
|
Guest123 Guest
|
|
Posted: Thu Oct 16, 2008 1:06 am |
|
|
I quickly changed the function from returning int1 to int8. However the behavior remains the same.
I think I will have to analyze what happens during the call of the function. Is it possible that the parameter gets corrupted if an interrupt occurs during the call of the function? |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Oct 16, 2008 2:20 am |
|
|
Quote: | Is it possible that the parameter gets corrupted if an interrupt occurs during the call of the function? |
This should never happen, but is possible due to errors in your code or compiler bugs. If the error is in your code, it should be searched at the interrupt functions rather than the main level.
Regards,
Frank |
|
|
Guest
|
|
Posted: Thu Oct 16, 2008 2:34 am |
|
|
Code: |
79: int1 getTimeout(struct timref_t* ref)
80: {
81: return ref->signal_flag;
03C4 0108 MOVLB 0x8
03C6 C8C7 MOVFF 0x8c7, 0xfe9
03CA C8C8 MOVFF 0x8c8, 0xfea
03CE 0E00 MOVLW 0
03D0 B0EF BTFSC 0xfef, 0, ACCESS
03D2 0E01 MOVLW 0x1
03D4 6E01 MOVWF 0x1, ACCESS
82: }
03D6 0100 MOVLB 0
03D8 0C00 RETLW 0
|
This is what the Compiler does with the code of the getTimeout function. I reinvestigated the problem with the ICD and when the function fails there is a wrong address in the 0x8c7 and 0x8c8 memory locations. Any suggestions?
Meanwhile I will have another look at my Interrupt Handlers. |
|
|
Ttelmah Guest
|
|
Posted: Thu Oct 16, 2008 3:08 am |
|
|
Look in particular at what is stored immediately in front of these locations in memory.
The 'common' cause of this type of problem, is a buffer or memory area overflowing, and corrupting something beyond. If you are using strings, triple check you are leaving space for the teminating character. If you are using arrays, triple check that you are applying 'bounds checking' in your own code, and indexes can never go outside the legal range. remember in particular that C uses zero-referenced arrays, so for 'array[10]', 9 is the highest legal address.
As a simple 'test', declare two extra 'dummy' variables. One in front, and one behind the problem value. So something like:
[code]
int32 dummy1=0;
struct timref_t myTimRef;
int32 dummy2=0;
[code]
Check in the symbol file that these are actually placed around the problem variable, then see if either of these changes.
Best Wishes |
|
|
Guest
|
|
Posted: Thu Oct 16, 2008 3:59 am |
|
|
Firstly, thanks for all your support I greatly appreciate it.
However the problem still persists. The memory area the data corruption occurs is not used by my variables it is managed by the compiler. Therefore it is very complicated to see if I overwrite the location by myself.
The problem seems to be that the timing is problematic. The function is used for several references (lets say myTimRef is located at 0x0792 and myTimRef2 is located at 0x0305). In this case as soon as the problem occurs the memory locations which corrupt (0x08x7 and 0x08c8) hold either 0x0305 or 0x0392 instead of 0x0792. |
|
|
Guest
|
|
Posted: Thu Oct 16, 2008 5:29 am |
|
|
I think that I have found the problem. The disassembly shows it. I have a function (unsetTimer) which is called during an Interrupt. The disassembly is the following:
Code: |
56: timstat_t unsetTimer(struct timref_t* ref)
57: {
58: unsigned int8 i = 0;
01F0 0108 MOVLB 0x8
01F2 6BC8 CLRF 0xc8, BANKED
59:
60: // --- Search the corresponding timer ---
61: while(timers[i]!=ref && i<NUM_TIMERS)
62: {
01F4 90D8 BCF 0xfd8, 0, ACCESS
01F6 35C8 RLCF 0xc8, W, BANKED
01F8 6A03 CLRF 0x3, ACCESS
01FA 0FCD ADDLW 0xcd
01FC 6EE9 MOVWF 0xfe9, ACCESS
01FE 0E00 MOVLW 0
0200 2003 ADDWFC 0x3, W, ACCESS
0202 6EEA MOVWF 0xfea, ACCESS
0204 CFEC MOVFF 0xfec, 0x8ca
0208 52ED MOVF 0xfed, F, ACCESS
020A CFEF MOVFF 0xfef, 0x8c9
020E 51C6 MOVF 0xc6, W, BANKED
0210 5DC9 SUBWF 0xc9, W, BANKED
0212 E103 BNZ 0x21a
0214 51C7 MOVF 0xc7, W, BANKED
0216 5DCA SUBWF 0xca, W, BANKED
0218 E005 BZ 0x224
021A 51C8 MOVF 0xc8, W, BANKED
021C 0809 SUBLW 0x9
021E E302 BNC 0x224
63: i++;
0220 2BC8 INCF 0xc8, F, BANKED
64: }
0222 D7E8 BRA 0x1f4
65:
66: if(i<NUM_TIMERS)
0224 51C8 MOVF 0xc8, W, BANKED
0226 0809 SUBLW 0x9
0228 E30F BNC 0x248
67: {
68: // --- Unset the timer ---
69: timers[i] = 0;
022A 90D8 BCF 0xfd8, 0, ACCESS
022C 35C8 RLCF 0xc8, W, BANKED
022E 6A03 CLRF 0x3, ACCESS
0230 0FCD ADDLW 0xcd
0232 6EE9 MOVWF 0xfe9, ACCESS
0234 0E00 MOVLW 0
0236 2003 ADDWFC 0x3, W, ACCESS
0238 6EEA MOVWF 0xfea, ACCESS
023A 6AEC CLRF 0xfec, ACCESS
023C 52ED MOVF 0xfed, F, ACCESS
023E 6AEF CLRF 0xfef, ACCESS
70: return TIMER_OK;
0240 0E00 MOVLW 0
0242 6E01 MOVWF 0x1, ACCESS
0244 D004 BRA 0x24e
71: }
72: else
0246 D003 BRA 0x24e
73: {
74: // --- The given Timer reference has not been found ---
75: return WRONG_TIMER_REF;
0248 0E02 MOVLW 0x2
024A 6E01 MOVWF 0x1, ACCESS
024C D000 BRA 0x24e
76: }
77: }
024E 0100 MOVLB 0
0250 0C00 RETLW 0
|
For reference here again the disassembly of the getTimeout function:
Code: |
79: int1 getTimeout(struct timref_t* ref)
80: {
81: return ref->signal_flag;
03C4 0108 MOVLB 0x8
03C6 C8C6 MOVFF 0x8c6, 0xfe9
03CA C8C7 MOVFF 0x8c7, 0xfea
03CE 0E00 MOVLW 0
03D0 B0EF BTFSC 0xfef, 0, ACCESS
03D2 0E01 MOVLW 0x1
03D4 6E01 MOVWF 0x1, ACCESS
82: }
03D6 0100 MOVLB 0
03D8 0C00 RETLW 0
|
As one can see the function unsetTimer accesses memory location 0x08c6 (@program location 020E) via a banked access. Therefore if the Interrupt (where unsetTimer is called) arises the passing parameter of getTimeout will get corrupted.
- Can anyone (who has a better understanding of assembly language) please confirm this?
- Where can such bugs (if it is really one) be reported and how can I work around this issue? |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Thu Oct 16, 2008 5:44 am |
|
|
If this is the case it is not a compiler bug but an error in your implimentation.
Your easiest option is to disable interrupts at the start of your getTimeout routine and re-enable them at the end.
this will also test your theory |
|
|
Guest
|
|
Posted: Thu Oct 16, 2008 6:01 am |
|
|
Well I do not agree in all terms with you. It is true that I can do a work around by disabling Interrupts while calling getTimeout. But in my eyes it is a Compiler Bug because I can't control which memory locations the compiler uses to pass the parameter (or whatever it does with these memory locations). A function which is called during an Interrupt (like unsetTimer is) should only use memory locations which exclusive to that function. Please let me know if I am completly wrong with my opinion. |
|
|
Guest
|
|
Posted: Thu Oct 16, 2008 6:04 am |
|
|
Additionally I dug a little deeper and this is what the symbol map of CCS-C says:
Code: |
8C7-8C8 strstr.s1
8C7-8C8 unsetTimer.ref
8C7-8C8 strlen.s
8C7-8C8 getTimeout.ref
8C7-8C8 strncmp.s1
8C7 app_processData.len
8C7 rm_runService.@SCRATCH1
8C7 pdata_read.@SCRATCH3
|
As one can see CCS-C allocates the passing parameter of unsetTimer and the passing parameter of getTimeout at the same memory location which leads to the data corruption. |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Thu Oct 16, 2008 7:20 am |
|
|
There seems to be alot of information missing from this problem.
Is it possible that the time ref passed to getTimeout is unset during the interupt routine.
The timer could interrupt at any time, during the interrupt you proberbly check and remove timers based on their timeout value. If the interrupt occured during the getTimeout routine then the timer you are checking could be unset.
Rather than me (and yes I do understand assembler) go through the assembler, please post the C code including the interrupt routine without the asm code, so we can have a look at that. |
|
|
Guest
|
|
Posted: Thu Oct 16, 2008 7:36 am |
|
|
Well what you say is possible that the timer gets unset during the call to the getTimeout function. But the information of the signal_flag is stored in the timref_t Variable and this one is still around and valid (it is declared in main from where the getTimeout function is called). Hereunder I post the complete code of my timer module.
Header File (timer.h):
Code: |
#ifndef _TIMER_H_
#define _TIMER_H_
/// Timer Reference structure
typedef struct timref_t
{
int1 signal_flag; ///< Holds the timer flag
unsigned int16 cnt; ///< Current counter value for the timer
unsigned int16 cmp; ///< Compare value for the timer
};
/// The status values returned by the timer functions
typedef enum timstat_t
{
TIMER_OK, ///< Last operation was ok
NO_FREE_TIMER, ///< There are no free timers available
WRONG_TIMER_REF ///< The timer reference provided could be found
};
/**
@brief Initializes the timer module
@return The result of the initialisation process
*/
timstat_t initTimer(void);
/**
@brief Initializes a new timer
@param[in] timeout Timer value in miliseconds
@param[in] ref Timer reference to be used
@return Result of the timer initialisation process
*/
timstat_t setTimer(unsigned int16 timeout, struct timref_t* ref);
/**
@brief Deletes a timer
@param[in] ref Timer reference which shall be deleted
@return Result of the timer deletion process
*/
timstat_t unsetTimer(struct timref_t* ref);
/**
@brief Checks if the Timeout flag of the provided reference has been risen
@param[in] ref Timer reference
@return Timeout flag value
*/
int1 getTimeout(struct timref_t*);
#endif
|
Code of the implementation file (timer.c):
Code: |
#include "timer.h"
#define NUM_TIMERS 10
struct timref_t* timers[NUM_TIMERS];
timstat_t initTimer(void)
{
unsigned int8 i;
for(i=0; i<NUM_TIMERS; i++)
{
timers[i] = 0;
}
#ifdef _PIC18F2620_
// --- Setup Timer2 to fire an Interrupt every milisecond @ Fosc = 32Mhz ---
T2CON = 0x0F;
PR2 = 249;
PIE1 |= 0x02;
#endif
return TIMER_OK;
}
timstat_t setTimer(unsigned int16 timeout, struct timref_t *ref)
{
unsigned int8 i=0;
// --- search unused timer ---
while(timers[i]!=0 && i<NUM_TIMERS)
{
i++;
}
// --- There is no timer left to be assigned ---
if(i >= NUM_TIMERS)
{
return NO_FREE_TIMER;
}
// --- Reset the timer reference values ---
ref->cnt = 0;
ref->cmp = timeout;
ref->signal_flag = 0;
timers[i] = ref;
return TIMER_OK;
}
timstat_t unsetTimer(struct timref_t* ref)
{
unsigned int8 i = 0;
// --- Search the corresponding timer ---
while(timers[i]!=ref && i<NUM_TIMERS)
{
i++;
}
if(i<NUM_TIMERS)
{
// --- Unset the timer ---
timers[i] = 0;
return TIMER_OK;
}
else
{
// --- The given Timer reference has not been found ---
return WRONG_TIMER_REF;
}
}
int1 getTimeout(struct timref_t* ref)
{
return ref->signal_flag;
}
/**
@brief Interrupt handler for the Timer 2 Compare interrupt
This interrupt handler is called every milisecond. As soon as one of the registered
timers reaches its timeout value the corresponding flag is being set.
*/
#INT_TIMER2
void timer2_int_handler(void)
{
int8 i;
// --- Execute for every timer ---
for(i=0; i<NUM_TIMERS; i++)
{
// --- Check if a timer is registered for this memory space ---
if(timers[i] != 0)
{
(*(timers[i])).cnt++;
// --- Has the timer reached its timeout value ---
if((*(timers[i])).cnt >= (*(timers[i])).cmp)
{
(*(timers[i])).signal_flag = 1; // Set Flag
timers[i] = 0;
}
}
}
}
|
I really hope that we can find the mistake. The work around (disabling interrupts during call to getTimeout) is not really an option because you can not do it inside the function. This means that you have to disable the interrupts then call getTimeout and finally reenable the interrupts... |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Thu Oct 16, 2008 7:53 am |
|
|
I cannot see where unsetTimer is called. It is not in the Timer2 interrupt!
I cannot see where getTimeout is called. Or where the parameter passed to getTimeout is defined and set.
I have no way of knowing from this if TIMER2 interrupt will affect your code.
Just that it could set a timer to 0 (null) which could well be the one you passed to getTimeout. |
|
|
Wayne_
Joined: 10 Oct 2007 Posts: 681
|
|
Posted: Thu Oct 16, 2008 8:04 am |
|
|
I assume you are allocating memory at some point for your timers which you pass to setTimer ?
Where are you deallocating it ?
No need to set the flag if you are then going to set the pointer to 0.
This does not free the memory! It just clears the pointer.
Code: |
(*(timers[i])).signal_flag = 1; // Set Flag
timers[i] = 0;
|
Last edited by Wayne_ on Thu Oct 16, 2008 8:06 am; edited 1 time in total |
|
|
|
|
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
|