|
|
View previous topic :: View next topic |
Author |
Message |
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
Bootloader>main soft: interrupts stop working |
Posted: Tue Jan 16, 2007 8:42 am |
|
|
Hi,
this topic is a continuation of the discussion in http://www.ccsinfo.com/forum/viewtopic.php?t=24301
Now this is my own topic on this issue.
I am implementing the CCS bootloader which is at the beginning of ROM space. I am able to download code and to reset and restart the bootloader. PIC16F877A and pcwh 3.226
bootloader.c:
Code: | #include <16F877A.h>
#fuses XT, PUT, NOWDT, NOPROTECT, NOLVP, NOCPD, NOWRT, NOBROWNOUT, NODEBUG
#define _bootloader
#include "bootloader.h"
#define LOADER_ADDR 0x0006
#org LOADER_ADDR, LOADER_END default
#use delay (clock=4000000)
#use rs232 (baud = 9600, parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8, ERRORS)
#include "my_loader.c"
void application (void);
#int_global
void pass_interrupt (void)
{
#asm
goto LOADER_END+5
#endasm
}
void main (void)
{
long timeout = 0;
int8 i, triggers_found = 0;
delay_us (50); // Transmit Buffer clearen
putc ('L');
for (i = 2; i > 0; i--)
{
// while ( !kbhit() && (++timeout < 50000) ) // 5 seconds
while ( !kbhit() && (++timeout < 0xFFFF) ) // 0xFFFF i.s.o. 50000 saves 5 bytes
delay_us (100);
if ( kbhit() )
{
if ( getc() == 170 )
triggers_found++;
else
break; // invalid char received, stop waiting.
}
else
break;
}
if ( triggers_found == 2 )
{
real_load_program ();
}
application (); // Jump to real application
}
#org LOADER_END+1,LOADER_END+100
void application(void) // Dummy functie, eigenlijk staat hier de échte main
{
while (TRUE);
} |
bootloader.h:
Code: | #define LOADER_END 0x1FF
#define LOADER_SIZE 0x17F
#ifndef _bootloader
#build ( reset = LOADER_END+1 , interrupt = LOADER_END+5 )
#org 0, LOADER_END {}
#endif |
When I download my main program via the ICD (without bootloader) everything works OK. However when I download my program via the bootloader some strange things happen:
* at the beginning of the program interrupts are enabled/seem working. I can receive bytes of the serial port, but when I enter my while (1) loop; interrupts seem to get disabled at a certain moment. Also some variables get changed after a couple of times looping and I don't understand why.
Does anybody have got an idea what's going on here? My main program can be downloaded;( it's quite large ):
http://users.[spam].be/chs/Werk/mainsoft.zip
grt |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Tue Jan 16, 2007 9:07 am |
|
|
by Ttelmah:
Quote: | Again, the problem of your 'adding' this problem to an existing thread, rather than launching a new one. The original posts were about a 18 chip, not a 16 chip.
Now, seriously, your interrupt handlers are damnably long. On the 16 chip, the edge of the first bank, is 0x7FF in the program memory. My guess, would be that something, is going over this boundary, and causing a problem with the bootloader present. Look to see if you can simplify what you are doing in the handler (both in terms of code volume, and tme involved...).
I'd suggest that you just run one software timer routine in timer_1, that perfoms the 32bit arithmetic, with the required 'wraps', to give seconds, and then do the tests inside this, for the individual counters being enabled, and handle their timers. Though 32 bit arithmetic is not 'too bad' inside an interrupt, it still takes about 20 instruction times, and adding up if all five routines trigger at once, you have something over 300 instructions in this interrupt alone, making it both large, and relatively slow. My suspicion is that with the bootloader present, one or more of the routines, is ending up having to perform a bank switch, and is becoming both larger, and slower as a result, leading to the problem. This is why it does not immediately occur, but takes a few characters.
Now, the number of characters you mention as the point where the problem shows, is suspiciously close to the size of your serial buffer. I'd again suggest altering the code for this. Either expanding the buffer to 16 characters, or if it will still be large enough, reducing it to 8. Then in the serial routine, instead of:
Read_Pointer = ( Read_Pointer + 1 ) % BUFFER_SIZE;
Use:
Read_pointer = (Read_pointer+1) & (BUFFER_SIZE-1);
The former, takes around 100 instruction cycles, and is relatively large. The latter takes only a handful of instructions, but will only work for 'binary' buffer sizes (8, 16 etc., characters).
Best Wishes |
1. I've tried it with reducing the timer_1 and timer_2 interrupt handlers with only 1 software timer; so having small ISR code. That gives the same problems.
2. However your comment sets me thinking that I have functions that are quite large. Perhaps that is a problem?
3.
Quote: |
Read_Pointer = ( Read_Pointer + 1 ) % BUFFER_SIZE;
Use:
Read_pointer = (Read_pointer+1) & (BUFFER_SIZE-1);
|
gives 0 change in ROM code.
4. BUFFER_SIZE is 64 |
|
|
Ttelmah Guest
|
|
Posted: Tue Jan 16, 2007 9:14 am |
|
|
Don't look at the ROM size, but look at the running length of the routine. Use the MPLAB simulator, and set the code up to jump to the interrupt handler. Record the stopwatch value at the entry to the routine, then at the exit. You should find something over 100 instruction times difference. If you do, it makes another important point. It implies that the integer division routine (used by the % function), is already being called in the main code. As such, interrupts will have automatically been disabled in the main code, whenever this function was being called....
The reason the ROM size didn't change, was that the routine was already 'present', so the interrupt handler just calls this routine, doesn't have to 'add' it.
Best Wishes |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Tue Jan 16, 2007 9:34 am |
|
|
Code: | .................... //Read_Pointer = ( Read_Pointer + 1 ) % BUFFER_SIZE; // Verhoog read-pointer
.................... Read_pointer = (Read_pointer+1) & (BUFFER_SIZE-1);
0388: MOVLW 01
0389: BCF 03.6
038A: ADDWF 4F,W
038B: ANDLW 3F
038C: MOVWF 4F |
vs
Code: | .................... Read_Pointer = ( Read_Pointer + 1 ) % BUFFER_SIZE; // Verhoog read-pointer
0388: MOVLW 01
0389: BCF 03.6
038A: ADDWF 4F,W
038B: ANDLW 3F
038C: MOVWF 4F
.................... //Read_pointer = (Read_pointer+1) & (BUFFER_SIZE-1); |
I'm sorry I don't understand why this gives exactly the same code then. Being faster does mean using lesser instructions, no?
Despite, this does not help the interrupt bug.. |
|
|
Ttelmah Guest
|
|
Posted: Tue Jan 16, 2007 10:53 am |
|
|
What has happened here, is that the optimiser has been very smart, and because you are using a binary size, and it is a constant, has converted to the '&' form. This is very good work by the optimiser (hurrah).
Best Wishes |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Wed Jan 17, 2007 2:02 am |
|
|
Is it possible to move the CCS bootloader in order to reside at the end of the ROM? What should be changed? |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Wed Jan 17, 2007 7:45 am |
|
|
Is it a problem to call a function inside a function that is also inside a function? |
|
|
treitmey
Joined: 23 Jan 2004 Posts: 1094 Location: Appleton,WI USA
|
|
Posted: Thu Jan 18, 2007 9:27 am |
|
|
It shouldn't be a problem. What you look at is the call stack depth. Look in the *.lst file and it show the worst case stack. compair that to the spec for the 16F877A |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Thu Jan 18, 2007 10:15 am |
|
|
Code: | CCS PCM C Compiler, Version 3.226, 28499 18-jan-07 17:13
Filename: C:\DOCUME~1\CHRIST~1.SEY\MYDOCU~1\Hardware\PIC\NE1A15~1\NS.LST
ROM used: 7223 words (88%)
Largest free fragment is 486
RAM used: 220 (60%) at main() level
264 (72%) worst case
Stack: 8 worst case (6 in main + 2 for interrupts) |
What does a stack of '8' mean?
What is the stack actually?
What if the stack is too big?
Can their be problems bootloader related? |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jan 18, 2007 7:55 pm |
|
|
For starters: Read the PIC manual, it has some good info on the stack.
Short: When you call a function there is a return statement at the end. How does the program know what address to return to? This is achieved by saving the address at time of calling the function. The address is saved into a special buffer memory, called the stack.
PIC processors have a limited size for the stack buffer, on PIC16 processors this is 8, larger on PIC18 processors.
Result of this is that you can only have a total of 8 'nested' functions (a function calling a function, calling a function, calling ... total 8x).
If the stack gets too large you are in trouble, it overflows into the first item (like a circular buffer). You than have a corrupted stack and your program will jump to unexpected memory locations. PIC18 has optional hardware for detecting an overflow, PIC16 will do nothing.
I ran your program in the simulator and there were strange program jumps after the Timer2 interrupts was activated. This was related to a corruption of the PCLATH register, a known bug and fixed somewhere between your v3.226 and v3.235 by introducing the jump_to_isr() function. For an explanation read http://www.ccsinfo.com/forum/viewtopic.php?p=75095
Change the interrupt function in your bootloader.c to Code: | #int_global
void pass_interrupt (void)
{
#if 0 // select this for compiler versions v3.235+
jump_to_isr(LOADER_END+5*(getenv("BITS_PER_INSTRUCTION")/8));
#else // older compiler versions
#asm
MOVWF 0x7F
SWAPF 0x03,W
CLRF 0x03
MOVWF 0x21
MOVF 0x0A,W // Save PCLATH
MOVWF 0x20
CLRF 0x0A
MOVLW 0x02
MOVWF 0x0A
goto LOADER_END+5
#endasm
#endif
} |
Because of the increased code size also change Code: | #define LOADER_ADDR 0x00010
|
|
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Fri Jan 19, 2007 2:41 am |
|
|
Hi,
thanks for the effort.. however the problem remains. I' ve tried your solution and it has no effect.
edit: I found that the modification does something wrong. The main program acts strange and delay_ms () function does not delay the exact time ( much longer )..
Also strange is ( also on prev bootloader ) that right after the download the program jumps to my application, then _no_ interrupts are working ( meaning I cannot receive any data on the serial port == serial interrupts ). When I reset, I can receive data a couple of times ( to leave the booting_00 () function and to go into my main function ) After that interrupts stop working. |
|
|
ferrumvir
Joined: 01 Feb 2006 Posts: 64 Location: England
|
|
Posted: Tue Jan 23, 2007 3:14 pm |
|
|
You might have seen that I've also been working on a bootloader.
Certain things I found may be your problem, I found the following did not work or caused problems.
1/
Code: |
#org LOADER_ADDR, LOADER_END default
#use delay (clock=4000000)
#use rs232 (baud = 9600, parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8, ERRORS)
|
I could not get the above to work, I had to keep the #use statments after the #device statment. I also moved them to the same location as you because otherwise certain "functions" inserted by the compiler were being located outside of boot load space, but the application then failed to run at all, so I was forced to move them back. I cured this my sequentially removing functions from the "main", once I'd found out which function it was I altered the logic so that I effectively moved the function into loader.c.
2/ There is also a "#org default" statment at the end of loader.c which does something, don't ask me what this does, but it must be important because this statment is also critical for the application to function afterward it's bee bootloaded. I removed it at one point because I thought it was being overridden by the next #org statement before being used, and things started to fail. I eventually checked the HEX files with and without this statement and as surprised by the difference.
3/ One test is to compile with a dummy application which just says "while(true);", there should only be 4 bytes of instructions for the application, no more. If there are then you need to look at the lst file and discover where they are being created.
4/ Another useful tip in finding the source of instructions is to remove the "#nolist" from the 16F877A.h file. This will provide you with additional information about instruction generation.
5/ Having had a look at your code, I also can't see a "#org" statement before the "main" routine. My best guess at your original question is that at some point your boot loader starts overwriting this function and that's why communications stop. This main routine must reside inside the boot loader.
6/ I found the most useful thing was writing a hex file interpretter, for the PC. Just going through the motion of parsing out the information and writing a humanly legible map of the PICs memory. Firstly this boosted my understanding of what was happening and secondly provided me a way of checking what was going on.
7/ PCM_Programmer also had a few tips see http://www.ccsinfo.com/forum/viewtopic.php?t=29573
Hope some of that helps.
Cheers Scott |
|
|
|
|
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
|