CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

RS232 firmware loader at runtime : same code, different size

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
D-Kens



Joined: 09 May 2005
Posts: 35
Location: Toulouse (France)

View user's profile Send private message MSN Messenger

RS232 firmware loader at runtime : same code, different size
PostPosted: Tue Jul 31, 2012 3:52 am     Reply with quote

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 ? Laughing

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. Confused

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

View user's profile Send private message AIM Address

PostPosted: Tue Jul 31, 2012 2:18 pm     Reply with quote

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)

View user's profile Send private message MSN Messenger

PostPosted: Wed Aug 01, 2012 12:45 am     Reply with quote

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: 19338

View user's profile Send private message

PostPosted: Wed Aug 01, 2012 2:44 am     Reply with quote

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)

View user's profile Send private message MSN Messenger

PostPosted: Wed Aug 01, 2012 5:38 am     Reply with quote

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. Sad
Ttelmah



Joined: 11 Mar 2010
Posts: 19338

View user's profile Send private message

PostPosted: Wed Aug 01, 2012 6:33 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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