|
|
View previous topic :: View next topic |
Author |
Message |
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
How to prevent inbuilt functions to disable interrupts? |
Posted: Thu Dec 29, 2005 1:22 pm |
|
|
Several inbuilt functions of the compiler disable interrupts to avoid data corruption problems. Examples of these functions are:
- read_eeprom
- strcpy
- memcpy
- etc
In my program I have a time critical High priority interrupt that I don't want to be disabled. I do see there are potential hazards, but in my interrupt I'm not using any of the above mentioned functions, so I should be safe.
Is it possible to change this behaviour of the compiler by setting an option?
If this option doesn't exists what other way is there to work around these limitations?
My current workaround is to stick with v3.226 which only disables the lower priority interrupts but I want to be able to upgrade to future versions as well. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Dec 29, 2005 1:52 pm |
|
|
I made a test program and compiled it with PCH vs. 3.241.
I didn't see memcpy() strcpy() turn off interrupts. Only
the read_eeprom() function does it.
You're looking for some sort of pragma that disables that
aspect of the CCS library code. I don't think there is one.
But the read_eeprom() code is so short, that you could
copy it and write your own function by using #byte and #bit
statements:
Code: | ..... c = read_eeprom(0);
012A: MOVFF FF2,41
012E: BCF FF2.7
0130: CLRF FA9
0132: BCF FA6.6
0134: BCF FA6.7
0136: BSF FA6.0
0138: MOVF FA8,W
013A: BTFSC 41.7
013C: BSF FF2.7
013E: MOVWF 40
Note: main.c address = 0x40
|
Test program:
Code: |
#include <18F452.h>
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
//#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#int_ext
void int_ext_isr(void)
{
char c;
c = 0x55;
}
//=============================
void main()
{
int8 source[20] = {"Hello World"};
int8 dest[20];
char c;
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
memcpy(dest, source, 20);
strcpy(dest, source);
c = read_eeprom(0);
while(1);
}
|
|
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Dec 29, 2005 6:40 pm |
|
|
Quote: | I made a test program and compiled it with PCH vs. 3.241.
I didn't see memcpy() strcpy() turn off interrupts. Only
the read_eeprom() function does it.
You're looking for some sort of pragma that disables that
aspect of the CCS library code. I don't think there is one.
But the read_eeprom() code is so short, that you could
copy it and write your own function by using #byte and #bit |
Check the errata for the processor you are using. I seem to remember there was a problem on the 18F family with the data in the holding register EEDATA being corrupted if not read immediately after the read EEPROM sequence. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Dec 29, 2005 6:55 pm |
|
|
They want at least one instruction between writing to the EEADR
register and setting the RD bit (EECON1.0)
http://ww1.microchip.com/downloads/en/DeviceDoc/80245a.pdf
The CCS code satifies the errata:
Code: | ... c = read_eeprom(0);
012A: MOVFF INTCON,@@41
012E: BCF INTCON.7
0130: CLRF EEADR // Write to EEADR
0132: BCF EECON1.6
0134: BCF EECON1.7
0136: BSF EECON1.0 // Set RD bit
0138: MOVF EEDATA,W
013A: BTFSC @@41.7
013C: BSF INTCON.7
013E: MOVWF c |
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Fri Dec 30, 2005 7:13 pm |
|
|
PCM programmer wrote: | I made a test program and compiled it with PCH vs. 3.241.
I didn't see memcpy() strcpy() turn off interrupts. Only
the read_eeprom() function does it. [/code] | Thanks PCM, until now I just assumed the CCS compiler always turned of the interrupts but it turns out to be a little more intelligent. It looks like the CCS compiler is disabling interrupts for all functions that use the TBLRD registers but I haven't figured out why, this requires some more investigations after the weekend. I keep you updated. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Mon Jan 02, 2006 10:56 am |
|
|
I just returned to work after the weekend and figured out the difference between my program and PCM's example: I copy the fixed data from Flash memory while PCM's example copies the data from RAM.
Here is the modified example program: Code: | #include <18f458.h>
#FUSES HS, NOPROTECT, PUT, NOBROWNOUT, NOWDT, NOLVP
#use delay(clock=16000000)
//=============================
void main()
{
int8 source[20] = {"Hello World"};
int8 dest[20];
char c;
memcpy(dest, source, 20); // No disabling of interrupts
strcpy(dest, source); // No disabling of interrupts
c = read_eeprom(0); // Disables interrupts, but very short time and I can live with this.
memcpy(dest, "Hello World", 12); // Disables interrupts
strcpy(dest, "Hello World"); // Disables interrupts
while(1);
} |
The memcpy that copies the data from ROM does so by using the TBLPTR registers. Even when you don't use interrupts in your program these functions will disable interrupts as a precaution. I don't know why the interrupts have to be disabled, could it be that an interrupt destroys the contents of the TBLPTR registers? |
|
|
Ttelmah Guest
|
|
Posted: Mon Jan 02, 2006 3:43 pm |
|
|
This is a fix, for a problem I had ages ago.
In 2002, using the 18F452, I had problems when an interrupt conatined a table based 'switch' instruction, and a TBLRD was used in the main. I sent CCS this email:
Quote: |
What is happening, is that once a read or write is set up, if an interrupt
occurs, which itself uses these registers, the actual operation may access
an incorrect memory address, resulting in either incorrect retrieval, or
corruption of memory data. Since you set up such instructions for constant
array accesses, or for certain types of switch statement, the chances of
data corruption are significant (or branching to an invalid address). I
suspect that this is explains the problems I was having with memory access.
I have 'trimmed' the code, and am attaching it here:
#include <18F452.h>
#fuses
H4,NOPROTECT,NOWRT,NOOSCSEN,BROWNOUT,BORV45,WDT,WDT1,PUT,CCP2C1,STVREN,NOLVP
,NODEBUG
#use delay(clock=40000000, restart_wdt )
int State = 0;
#define InPin PIN_B0
#define OutPin PIN_B7
const int Array[]={ 1, 2, 3, 4 };
#int_ext
void
InterruptExt( void )
{
switch( State ) {
case 0:
output_bit( OutPin, 1 );
State = 1;
break;
case 1:
output_bit( OutPin, 0 );
State = 0;
break;
}
}
void
main( void )
{
int a;
int b;
output_bit( OutPin, 0 );
ext_int_edge(0, L_TO_H );
enable_interrupts( INT_EXT );
Enable_interrupts(GLOBAL);
while ( TRUE ) {
restart_wdt();
b = Array[a];
}
}
If you look at the generated assembler, you can see where the problem would
occur, in the interval between setting up the table read (for the constant
data array), and actually completing the transfer.
What is needed, is that interrupts are going to have to be disabled in the
'core' of all the table access functions, or the interrupt handler would
have to be modified to store and restore the status of these registers...
|
Part of the code showing the fault, was actually from another poster here.
CCS added code to disable interrupts around the TBLRD functions at this time. They still do not save these registers in the interrupt handler (takes quite a time to save and restore these), so the fix remains in place. That it is not there on the mem to mem copy, suggests that this problem might actually still exist for these routines...
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Mon Jan 02, 2006 5:32 pm |
|
|
Quote: | CCS added code to disable interrupts around the TBLRD functions at this time. They still do not save these registers in the interrupt handler (takes quite a time to save and restore these), so the fix remains in place. | Thanks TTelmah, I'm thankful that people like you with historic info on the CCS compiler are hanging around at this forum.
Quote: | That it is not there on the mem to mem copy, suggests that this problem might actually still exist for these routines... | I checked the memcopy routines, they are save. The first example copies from RAM to RAM using the FSR0 registers which are saved in the default interrupt handlers. The second example copies from Flash to RAM with disabled interrupts so is save as well.
I'm not sure if the CCS solution poses a problem to me, I think it is allright. I have a time critical fast interrupt in my code and I was wondering why it was disabled as it doesn't do anything with the TBLRD registers. Optimizing my code I also noticed my 4 byte ROM constants to require almost 30 instructions initialization code each.....
Maybe I'll write my own memcopy function which only disables the low priority interrupts, but I hate it when I have to move away from the standard C code.
Thanks everyone for helping me out on this strange behavior. |
|
|
Ttelmah Guest
|
|
Posted: Tue Jan 03, 2006 4:20 am |
|
|
The real 'pity', is that the behaviour is not controlled by a flag, rather than defaulting. If the compiler simply tested whether any code inside the interrupt used TBLRD, and only if so, then enabled the behaviour, everyone would be happy. :-)
There are quite a few places like this, where default behaviours can be annoying.
I found myself thinking 'I remember something about this', when the behaviour was mentioned, and it took me a while to look through my email archive, and find the old mail.
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Jan 03, 2006 7:44 am |
|
|
Trying to write my own memcpy function I failed because the CCS compiler doesn't allow me to pass a pointer to a constant in ROM. Aarghh... I knew it, but that's exactly why I was writing my own memcopy function in the first place! A chicken & egg problem...
I discovered the printf function to disable all interrupts as well so we now have the following list of functions disabling interrupts:
- read_eeprom
- write_eeprom
- memcpy(dest, constant string)
- strcpy(dest, constant string)
- printf(constant string)
I don't want to rewrite all these functions so I'll stick with v3.226 which only disables the normal interrupts but leaves the Fast interrupts alone...
Thanks everyone. |
|
|
Ttelmah Guest
|
|
Posted: Tue Jan 03, 2006 10:33 am |
|
|
Does the putc work-around?.
So:
Code: |
void putmsg(int chr) {
putc(chr);
}
//Then in the code
putmsg("Test Message");
|
I think you will find this transfers the message with the interrupts left enabled.
The same trick, can be used to move a constant string toa buffer, with:
Code: |
int locn;
#define resetptr locn=0
void tobuff(int8 buff[],int chr) {
buff[locn++]=chr;
}
//Then in the main code
resetptr;
tobuff(array,"Test message");
//Moves the constant message to the array
|
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Jan 04, 2006 5:27 pm |
|
|
Hi Ttelmah,
Thanks for the putc work-around idea. I haven't tried it because I've already spent too much time to this issue and using v3.226 will do for the moment. I don't have a good feeling about writing my own versions of the standard functions, maybe I'll ask CCS to add a new compiler option for disabling the extra code. Fast interrupt support in the compiler is a mess already anyway. |
|
|
Guest Guest
|
Delay too! |
Posted: Thu Jan 26, 2006 5:42 pm |
|
|
Delay_ms switches off GIE - presumably to time things accurately. But int_rda2's that occur during the delay don't seem to fire their interrupt once GIE is set again.
Looked at the irq logic chain in Chapter 9 if the 6621's pdf - its all combinatorial, so there should be a rising edge to cause an interrupt when GIE is set again - any of you been through this process with analysing the other funcs that switch irq's off?
Ken Macfarlane |
|
|
Ttelmah Guest
|
|
Posted: Fri Jan 27, 2006 3:33 am |
|
|
Delay_ms, _does not_ turn off the interrupts, _unless_ you are using delays inside your interrupt handler. PCM_programmer, has published a method in the past, of forcing the compiler to generate two copies of the routine to stop this. _Every_ function (including your own), will have interrupts disabled, if the same routine is used inside and outside the interrupts (this is needed to prevent re-entrancy problems). You can always prevent this, by having two copies of the routine. The 'point' about this thread, was that a few functions, disable interrupts, even when they are not being used in both locations.
Have you got 'ERROR' enabled on the RDA2 UART?. If not, then the interrupt _will_ fire once, but then never again, since the UART will now be stuck in the 'overrun error' condition.
Best Wishes |
|
|
|
|
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
|