|
|
View previous topic :: View next topic |
Author |
Message |
D-Kens
Joined: 09 May 2005 Posts: 35 Location: Toulouse (France)
|
RS232 firmware loader at runtime : same code, different size |
Posted: Tue Jul 31, 2012 3:52 am |
|
|
Good morning.
I just finished adapting CCS loader library (based on EdWaugh's code posted there : http://www.ccsinfo.com/forum/viewtopic.php?t=39179&highlight=firmware+update) for my own purpose, and I now have a file that I can include in my projects for "program memory self-programming over RS232 at runtime". But, of course, I still have a problem... Else, why would I be posting ?
Code: | // Define the size of the loader in ROM and the address to write it to
#ifndef LOADER_END
#define LOADER_END getenv("PROGRAM_MEMORY")-1 // Get the end of the program memory and put the loader there
#define LOADER_SIZE 0x5BF // Size of the loader functions 0x43F
#endif
#define LOADER_ADDR (LOADER_END-LOADER_SIZE) // Address of the loader
// Set all the functions following this directive to be included in the
// loader ROM area
#ORG LOADER_ADDR+10, LOADER_END default
// Serial port stream specific to this area to make the compiler create
// specific specific serial functions stored in the #ORG
#use rs232(baud=19200, parity=N, xmit=PIN_C6, rcv=PIN_C7, bits=8, errors, brgh1Ok, stream=COM1, RESTART_WDT)
/* DEFINITION DES CONSTANTES */
#define BUFFER_LEN_LOD 64 // Length of a line in an Intel 8-bit hex file
#define EOT 0x04 // End of transmission
#define ACKLOD 0x06 // Acknowledge the last line (TRUE)
#define XON 0x11 // Turn transmission ON
#define XOFF 0x13 // Turn transmission OFF
#define NACKLOD 0x15 // Acknowledge the last line (FALSE)
/* */
// Convert two hex chars to a byte
// The SEPARATE directive tells the compiler not to inline this function, this reduces the ROM space required
#SEPARATE
unsigned int atoi_b16(char *s)
{ unsigned int result=0 ;
int i ;
// Convert two hex characters to a int8
for (i=0 ; i<2 ; i++,s++) {
if (*s>='A') { result = 16*result + (*s) - 'A' + 10 ; } // 0x37='A'+10
else { result = 16*result + (*s) - '0' ; } // 0x30='0'
}
return(result) ;
}
/* */
void real_load_program (short origine_appel)
{
short done=FALSE, do_ACKLOD ;
unsigned int checksum, line_type, dataidx, i, count, buffidx ;
unsigned int data[32] , buffer[BUFFER_LEN_LOD] ;
unsigned int16 l_addr, h_addr=0 ;
unsigned int32 addr ;
char extract ;
// Only required for parts where the flash erase and write sizes are different
#if (getenv("FLASH_ERASE_SIZE") > getenv("FLASH_WRITE_SIZE"))
unsigned int32 next_addr=0 ;
#endif
/* */
buffidx=0 ;
do {
buffer[buffidx]=fgetc(COM1) ;
} while (buffer[buffidx]!=':') ;
buffidx++ ;
/* */
while (!done) // Loop until the entire program is downloaded
{
do {
extract=fgetc(COM1) ;
if (extract!='\n') buffer[buffidx++]=extract ;
} while ((extract!=0x0D) && (buffidx<=BUFFER_LEN_LOD)) ;
fputc(XOFF, COM1) ; // Envoi du XOFF pour interrompre la transmission
do_ACKLOD=TRUE ;
//buffer[buffidx]='\0' ; // TEST
//fprintf(COM2, "%s\r\n", buffer) ; // TEST
// Only process data blocks that start with ':'
if (buffer[0]==':') {
count=atoi_b16(&buffer[1]) ; // Get the number of bytes from the buffer
l_addr=make16(atoi_b16(&buffer[3]), atoi_b16(&buffer[5])) ; // Get the lower 16 bits of address
line_type=atoi_b16(&buffer[7]) ;
addr=make32(h_addr, l_addr) ; // At the first time through h_addr is zero as we are assuming
// the high bytes of the addr are zero until we get a type 4 command
/* */
if (line_type==1) { // If the line type is 1, then data is done being sent
done=TRUE ;
do_ACKLOD=TRUE ;
}
else {
if (addr<LOADER_ADDR || addr>LOADER_END) { // Don't try to overwrite the loader or the configuration bits
checksum=0 ;
for (i=1 ; i<(buffidx-3) ; i+=2) { checksum += atoi_b16(&buffer[i]) ; }
checksum=0xFF-checksum+1 ;
if (checksum != atoi_b16(&buffer[buffidx-3])) { do_ACKLOD=FALSE ; }
else {
if (line_type==0) {
// Loops through all of the data and stores it in data
// The last 2 bytes are the check sum, hence buffidx-3
for (i=9, dataidx=0 ; i<buffidx-3 ; i+=2) { data[dataidx++]=atoi_b16(&buffer[i]) ; }
#if (getenv("FLASH_ERASE_SIZE") > getenv("FLASH_WRITE_SIZE"))
if ((addr!=next_addr)&&(addr&(getenv("FLASH_ERASE_SIZE")/2-1)!=0)) { erase_program_eeprom(addr) ; }
next_addr=addr+1 ;
#endif
write_program_memory(addr, data, count) ; // Attempt a write to the program memory
}
else {
if (line_type==4) {
h_addr=make16(atoi_b16(&buffer[9]), atoi_b16(&buffer[11])) ;
}
}
}
} // Fin du "if ((addr<LOADER_ADDR || addr>LOADER_END) && addr<0x300000)"
} // Fin du else associé à "if (line_type==1)"
} // Fin du "if (buffer[0]==':')"
/* */
if (!origine_appel) {
if (do_ACKLOD) { fputc(ACKLOD, COM1) ; }
else { fputc(NACKLOD, COM1) ; }
}
/* */
if (!done) { fputc(XON, COM1) ; }
buffidx=0 ;
restart_wdt() ;
}
/* */
if (!origine_appel) { fputc(EOT, COM1) ; }
reset_cpu() ;
}
/* */
#ORG default
#ORG LOADER_ADDR, LOADER_ADDR+9
void load_program(short origine_appel )
{
real_load_program(origine_appel) ;
}
|
I compile with PCWH v4.124, and the target PIC is a 18F46K20...
I first worked on that library in a small independent project, and the size of the "protected area" was 0x3FF. But then, when I included the same file in a larger project, the compiler said a section of the code was too large for that area, and I had to use a larger "protected area", even though it's exactly the same code.
My guess is that it's a matter of optimization : in a larger project, the compiler has less freedom for optimizing code, hence the larger firmware. My fear is that, in a near (or not so near) future, the compiler might ask me to change the size of the protected area again, and it's not acceptable : by definition, the protected area is protected and not rewrited. Once programmed (by ICSP) I'm not supposed to change it ! The final aim of that firmware is to enable remote firmware updates over a GSM connection, fully automated : a change in the protected area would imply the need for a human operation on location.
As the instructions of my library managed to enter a smaller protected area, ain't there a way to force the compiler to optimize code for that section to always fit the 0x3FF size, and to do its best with the rest of the firmware, instead of trying to optimize the whole code ?
Any help/idea would be appreciated. Thanks,
Christophe. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Jul 31, 2012 2:18 pm |
|
|
I can't comment on the size issue but - what is
RESTART_WDT ??
Does this imply you are running the WDT during a boot load ??
I don't think I would want to do that actually
as it seems a short path to a messed up operation.
There is a lot of processing with a very RARE // single explicit ,
reset_wdt op happening.
( to clarify - the thing that i can imagine going wrong is the WDT activating and corrupting the whole operation ) |
|
|
D-Kens
Joined: 09 May 2005 Posts: 35 Location: Toulouse (France)
|
|
Posted: Wed Aug 01, 2012 12:45 am |
|
|
My bad !!!! Before calling the function, I obviously disable all interrupts, and I made several attempts with or without the WDT...
Actually, having the watchdog activated doesn't seem to have any incidence on the process : the operation is not corrupted, as you said. My concern is really not about the process itself, because I have a piece of code that yet suits my needs, but about how to be sure it will always use the same space in ROM (even in future projects).
I included that library in 2 very different projects, compiled and opened the LST files. For the same C code, the assembly can be very different (due to optimization purposes I suppose). I thought about inserting ASM directly into my code, and that's probably the next thing I'm gonna try. I'll copy/paste the ASM commands from the most optimized project I have and copy it in the other project between #ASM ASIS and #ENDASM tags... If I'm not mistaken, the #ASM ASIS directive means the compiler can't perform optimization on that part of code and must use it "as is" : hence it should be the same in every project, shouldn't it ?
Regards,
Christophe. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Wed Aug 01, 2012 2:44 am |
|
|
You seem to be talking as if you are trying to compile the bootloader with the project?.
This is not normally how it is done.
The main project _must_ have a build statement relocating _it_ totally after the bootloader, and be built separately. Otherwise it is almost impossible to ensure that all functions common to both get separate code. It is _possible_ to build them together, but very difficult indeed to guarantee that no cross sharing takes place.
The normal proceedure if you want a single 'result' hex file that can be programmed including both code and bootloader, is to merge them, either with MPLAB, with the programmer software, or with an #import statement after building them separately.
Best Wishes |
|
|
D-Kens
Joined: 09 May 2005 Posts: 35 Location: Toulouse (France)
|
|
Posted: Wed Aug 01, 2012 5:38 am |
|
|
Ttelmah, you're right : I'm trying to compile the loader with the project. I know it's not the "academic" way to deal with a bootloader, but my firmware is a serial loader and not a bootloader : I want the main application to be able to launch a firmware update at any time, and not just at boot.
Sorry, I'm not very familiar with all those memory issues. I assume the loader code must be written once for good in the PIC at a specific place accessible to anyone, but I don't see how to do so without including a call to the function (and the library that goes with it) in every project. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Wed Aug 01, 2012 6:33 am |
|
|
A 'serial loader', _is_ a bootloader....
The term 'boot', in computerese, comes from the idea of 'pulling yourself up by your bootstraps'. Running a program that in some way loads more code.
Historically, this was a hardwired tiny program (built on a mechanical patch panel), which loaded typically from punch tape, the 'main' code for the computer. This was the 'bootloader', and this operation became called 'booting up'. Even on chips like the PIC, where the code is not loaded, but in the ROM, waking the chip 'up' became 'booting'.
A bootloader, is any program that sits inside a chip, and loads code into the chip.
In your case, it is just not called at 'bootup'. The relocation issues are exactly the same as for a normal bootloader, and it has to be built the same way.
It has to reside in a known area of memory, and be completely standalone. The main code has to be compiled 'knowing' about this reserved area (and not overwriting it).
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
|