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

PIC18F8722 Bootloader at End of Program Memory

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PIC18F8722 Bootloader at End of Program Memory
PostPosted: Thu Oct 12, 2017 6:07 pm     Reply with quote

Good day.

I am writing a bootloader firmware using a PIC18F8722 and I prefer the bootloader firmware be located at the end of the program memory space. Below is my suggested memory organization.
Code:

|------------------------------------|
|    Bootloader Boot Vector          |   00000
|------------------------------------|
|                                    |
|------------------------------------|
|    Hi-Prio Interrupt Vector        |   00008
|------------------------------------|
|                                    |
|------------------------------------|
|    Lo-Prio Interrupt Vector        |   00018
|------------------------------------|  -------
|                                    |     ^
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     |
|    Application Code                |     |
|                                    |     |
|                                    |     |
|                                    |   Application
|                                    |   Memory Space
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     *
|------------------------------------|  -------
|                                    |     ^    1FB00
|                                    |     |
|                                    |     |
|    Bootloader Code                 |     |
|                                    |     |
|                                    |     |
|                                    |   Bootloader
|                                    |   Memory Space
|------------------------------------|     |
|    Application Boot Vector         |     |
|------------------------------------|     |
|                                    |     *
|------------------------------------|   -------  1FFFF

My concern here is that I do not want my Bootloader Boot Vector to be overwritten by the Application Boot Vector. The reason is, I will write the commands in the Bootloader code section. Thus, below is the code that will make sure the address 0x00000 will not be overwritten, although I am not sure if it is the correct implementation and I need feedback from the group for their suggestions.

Did I miss something that it should be included in the code?

Code:

                        if (Addr == 0x0)
                        {
                            write_program_eeprom(AppBootVector, Data[0]);
                            for (DataIdx = 1;
                                 DataIdx < Count;
                                 DataIdx++)
                            {
                                write_program_eeprom(Addr + DataIdx, Data[DataIdx]);
                            }
                        }
                        else
                        {
                            write_program_memory(Addr, Data, Count);
                        }

Below are my source files:
bootloader.h - a modified version that came with CCS
Code:

#if !defined(__BOOT_LOADER_H__)
#define __BOOT_LOADER_H__


#define LOADER_END              (getenv("PROGRAM_MEMORY") - 1)

#if (getenv("PROGRAM_MEMORY") % getenv("FLASH_ERASE_SIZE")) == 0
#define LOADER_SIZE             (0x4FF)
#else
#define LOADER_SIZE             (getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")) - 1)
#endif


#define LOADER_ADDR             (LOADER_END - LOADER_SIZE)


#ifndef _BOOTLOADER_
#org LOADER_ADDR, LOADER_END {}
#else
#org LOADER_ADDR, LOADER_END default
#endif


#endif


fwloader.c - my main() method including the modified real_load_program() method that came from CCS.

Code:

#include <18F8722.h>
#device ADC=16

#FUSES H4                        // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT                 // No code protection from reading
#FUSES NOWDT                     // No Watch Dog Timer
#FUSES NOBROWNOUT                // No brownout reset
#FUSES NOLVP                     // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT                       // power-up timer


#define BOOTLOADER_RUN          (PIN_A1)
#define BOOTLOADER_LED          (PIN_A2)

#define ACKLOD                  (0x06)
#define XON                     (0x11)
#define XOFF                    (0x13)

#define BUFFER_LEN_LOD          (64)


#define _BOOTLOADER_
#include "bootloader.h"


#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)


unsigned int atoi_b16 (char *BuffPtr)
{
    // Convert two hex characters to a int8
    unsigned int Result = 0;
    int Idx;

    for (Idx = 0;
         Idx < 2;
         Idx++, BuffPtr++) 
    {
        if (*BuffPtr >= 'A')
        {
            Result = 16 * Result + (*BuffPtr) - 'A' + 10;
        }
        else
        {
            Result = 16 * Result + (*BuffPtr) - '0';
        }
   }

   return Result;
}

void real_load_program (unsigned int32 AppBootVector)
{
    int  BuffIdx;
    char Buffer[BUFFER_LEN_LOD];
   
    int1  DoAcklod, Done = FALSE;
    int8  Checksum, LineType;
    int16 Laddr, Haddr = 0;
    int32 Addr;

    #if getenv("FLASH_ERASE_SIZE") > 2
    int32 NextAddr;
    #endif

    int8  DataIdx, Idx, Count;
    int8  Data[32];

    while (Done == FALSE)       // Loop until the entire program is downloaded
    {
        // Read into the buffer until 0x0D ('\r')
        // is received or the buffer is full
        BuffIdx = 0;
        do
        {
            if (BuffIdx >= BUFFER_LEN_LOD)
            {
                BuffIdx = 0;
            }
           
            Buffer[BuffIdx] = getc();
            if (Buffer[BuffIdx] == ':')
            {
                Buffer[0] = Buffer[BuffIdx];
                BuffIdx = 0;
            }
        } while (    (Buffer[BuffIdx++] != 0x0D)
                  && (BuffIdx <= BUFFER_LEN_LOD));

        putchar(XOFF);  // Suspend sender
        DoAcklod = TRUE;

        // Only process data blocks that start with ':'
        if (Buffer[0] == ':')
        {
            // Get the number of bytes from the buffer
            Count = atoi_b16(&Buffer[1]);
            // Get the lower 16 bits of address
            Laddr = make16(atoi_b16(&Buffer[3]), atoi_b16(&Buffer[5]));
            // Get the line type
            LineType = atoi_b16(&Buffer[7]);
            // Get the program memory address
            Addr = make32(Haddr, Laddr);

            // If the line type is 1, then data is done being sent
            if (LineType == 1)
            {
                Done = TRUE;
            }
            else if (    (    Addr < LOADER_ADDR
                           || Addr > LOADER_END)
                      && Addr < 0x300000)
            {
                Checksum = 0;   // Sum the bytes to find the check sum value
                for (Idx = 1;
                     Idx < (BuffIdx - 3);
                     Idx += 2)
                {
                    Checksum += atoi_b16(&Buffer[Idx]);
                }

                Checksum = 0xFF - Checksum + 1;
                if (Checksum != atoi_b16(&Buffer[BuffIdx - 3]))
                {
                    DoAcklod = FALSE;
                }
                else
                {
                    if (LineType == 0)
                    {
                        // Loops through all of the data and stores it in data
                        // The last 2 bytes are the check sum, hence buffidx-3
                        for (Idx = 9, DataIdx = 0;
                             Idx < (BuffIdx - 3);
                             Idx += 2)
                        {
                            Data[DataIdx++] = atoi_b16(&Buffer[Idx]);
                        }

                        #if getenv("FLASH_ERASE_SIZE") > 2
                        if (    (Addr != NextAddr)
                             && (Addr & (getenv("FLASH_ERASE_SIZE") / 2 - 1) != 0))
                        {
                            erase_program_eeprom(addr);
                        }
                       
                        NextAddr = Addr + 1;
                        #endif

                        // (*) Modifing the Application Boot Vector
                        // The assembly commands in memory are called the boot
                        // vector. It resets the board and points to the address of
                        // the main function.
                       
                        // Each HEX file has their own version of this command. But
                        // since we always want the boot loader to run before the
                        // user code, we must not overwrite the loader's boot vertor.
                        // Instead, we move the user's boot vector to another location
                        // within the boot loader's program area
                        // (see label "APP_BOOT_VECTOR" below).
                       
                        // This 'if' command detects user's boot vector and redirects it
                        // The boot vector is at address 0x0000
                        if (Addr == 0x0)
                        {
                            write_program_eeprom(AppBootVector, Data[0]);
                            for (DataIdx = 1;
                                 DataIdx < Count;
                                 DataIdx++)
                            {
                                write_program_eeprom(Addr + DataIdx, Data[DataIdx]);
                            }
                        }
                        else
                        {
                            write_program_memory(Addr, Data, Count);
                        }
                    }
                    else if (LineType == 4)
                    {
                        Haddr = make16(atoi_b16(&Buffer[9]), atoi_b16(&Buffer[11]));
                    }
                    else
                    {
                        ;;;;  // just do nothing here
                    }
                }   // else
            }   // else if
            else
            {
                ;;;;  // just do nothing here
            }
        }   // end if

        if (DoAcklod == TRUE)
        {
            putchar(ACKLOD);
        }
       
        putchar(XON);
    }   // end while

    putchar(ACKLOD);
    putchar(XON);
    return;
}

void main (void)
{
    unsigned int32 AppBootVector;               // the application boot vector address
   
    setup_adc_ports(NO_ANALOGS);                // setup PortA to digital I/O
   
    if (input(BOOTLOADER_RUN) == TRUE)          // firmware upgrade button is enabled
    {
        // get the memory address of the application boot vector
        AppBootVector = label_address(APP_BOOT_VECTOR);
       
        output_high(BOOTLOADER_LED);            // turn-ON bootloader LED
       
        // write new firmware to program memory
        real_load_program(AppBootVector);                   
        output_low(BOOTLOADER_LED);             // turn-OFF bootloader LED
    }
   
    //-----------------------------------------------------------------
    // this is a reboot command written in assembly.
    // these will be overwritten with the boot vector we obtain from
    // the new firmware HEX file.
    // See (*) above for more details
    //-----------------------------------------------------------------
    APP_BOOT_VECTOR:
    #asm
    GOTO 0x00000
    NOP
    NOP
    NOP
    #endasm
   
    return;
}



Thank you.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Fri Oct 13, 2017 3:21 am     Reply with quote

If you look at the data sheet, the code protection options for boot-block all allow you to protect the low addresses of memory, not the high address (just to confuse they talk about 'high memory', as the memory nearest address 0).
Look at table 25-5, for the layout that MicroChip assume will be used.

The option to build the CCS code for high memory address bootloaders is normally reserved for chips that are designed for this (PIC24 etc..).

Now the advantage of the 'high address' bootloader, is that it saves a jump time in interrupt handling, when the interrupt code can be directly accessed, instead of needing to be re-vectored. The big downside is that if anything goes wrong when writing the bottom block, the chip is toast. It is a pity that the interrupt vectors are not in another memory page than the boot vector, which would avoid this problem. It isn't really any more logical, and brings some downsides:
Lack of memory protection for the bootloader.
Need to update the application address separately. If this is stored at the end of memory the booloader code can't be protected.
Costs an erasable of memory at the top of the ROM for this.

Now there is a way to avoid the second and third of these.
If you are happy to allocate permanently enough space for your interrupt routines, with some 'spare', you can alter your build for your program like:

Code:

#build (interrupt = 0x008:0x3FF, reset=0x400)

The 'start' vector for your main code will then permanently be at address 0x400, with the interrupt code below this.
So long as you then just ensure that if anything reprograms the first block, the booloader entry vector is written, you have no need to store an 'application address'.
Obviously you potentially lose some memory between the area allocated for the interrupt handlers and the main code, but I think this is tidier than having the extra store at the top of memory. You can then set the code protection bit for the top block of your program memory and protect the bootloader code.
Even neater, the one write that has to change will only happen when the interrupt code is changed. You can distribute the 'main' program without any interrupt code for 99% of updates, and this doesn't need to happen. A lot safer.

A thought.
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Fri Oct 13, 2017 7:54 pm     Reply with quote

Hi Ttelmah,

I checked the PIC18F8722 datasheet and I can't find Table 25-5 and my guess is you are referring to Figure 25-5 for code protected program memory organization.

Now, back to your suggestion on your reply. If I understand it correctly, you suggest that my application code will have a start vector of 0x400 and the interrupt vector for the application code will be from 0x008 to 0x3FF via the #build CCS C compiler preprocessor.

What will be in address 0x000? Is this address the bootloader boot vector? What about my bootloader code? Where do I put it? Based on your comment below, the end part of the program memory is normally reserved for chips that are designed for this like PIC24.

Although, the way I understand it, the bootloader code will still be at the end but the application code will start at 0x400. See memory organization below.

Code:

|------------------------------------|
|    Bootloader Boot Vector          |           0x00000
|------------------------------------|
|                                    |
|------------------------------------|
|                                    |           0x00008
|                                    |
|    Interrupt Vector                |
|                                    |
|                                    |           0x003FF
|------------------------------------|  -------  0x00400
|    Application Boot Vector         |     ^
|------------------------------------|     |
|                                    |     |
|                                    |     |
|    Application Code                |     |
|                                    |     |
|                                    |     |
|                                    |   Application
|                                    |   Memory Space
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     |
|                                    |     *
|------------------------------------|  -------  0x1FB00
|                                    |     ^
|                                    |     |
|                                    |     |
|    Bootloader Code                 |     |
|                                    |     |
|                                    |     |
|                                    |   Bootloader
|                                    |   Memory Space
|                                    |     |
|                                    |     *
|------------------------------------|   -------  0x1FFFF


Did I get your suggestion correctly?
Thanks.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Fri Oct 13, 2017 11:37 pm     Reply with quote

Exactly.
Key is that provided you make the interrupt area large enough (you may need more than 0x400 - depends what you are doing), the application entry need never move.
At address zero you would need a fixed jump to your bootloader code.
The other thing is though that if you are distributing the code, given how rarely interrupt stuff needs to change, you can simply not include the interrupt code with the distribution, so the interrupt (and the bootloader jump) are not re-programmed.
This is quite a common way of distributing stuff. Smile
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Sat Oct 14, 2017 5:23 am     Reply with quote

Hi Ttelmah,

I created a simple application firmware that will align with what was discussed. Below is the application code and the bootloader header.

bootloader.h
Code:

#if !defined(__BOOTLOADER_OP2_H__)
#define __BOOTLOADER_OP2_H__


#define LOADER_END               (getenv("PROGRAM_MEMORY") - 1)

#if (getenv("PROGRAM_MEMORY") % getenv("FLASH_ERASE_SIZE")) == 0
#define LOADER_SIZE              (0x4FF)
#else
#define LOADER_SIZE              (getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")) - 1)
#endif

#define LOADER_ADDR              (LOADER_END - LOADER_SIZE)

#define APP_INT_VECTOR_START     (0x008)
#define APP_INT_VECTOR_END       (0x3FF)
#define APP_BOOT_VECTOR          (APP_INT_VECTOR_END + 0x1)

#ifndef _BOOTLOADER_
#build (reset=APP_BOOT_VECTOR, interrupt=APP_INT_VECTOR_START:APP_INT_VECTOR_END)
#org LOADER_ADDR, LOADER_END {}
#else
#org LOADER_ADDR, LOADER_END default
#endif


#endif



application_fw.c
Code:

#include <18F8722.h>
#device ADC=16

#FUSES H4                        // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT                 // No code protection from reading
#FUSES NOWDT                     // No Watch Dog Timer
#FUSES NOBROWNOUT                // No brownout reset
#FUSES NOLVP                     // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT                       // power-up timer

#include "bootloader_op2.h"

#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)


void main (void)
{
    unsigned int8 Numb = 0;
   
    delay_ms(100);
    printf("nApplication Version 1.0\n");

    while (TRUE)
    {
        printf("%u ", ++Numb);
    }

    return;
}



After compiling the said sources, I look at the listing and it is really weird because address 0x0000 has been replaced with DATA. Below is the snippet of the C/ASM listing.

Code:

00400:  GOTO   0106
.................... #include <18F8722.h>
.................... //////////// Standard Header file for the PIC18F8722 device ////////////////
.................... ///////////////////////////////////////////////////////////////////////////
.................... ////        (C) Copyright 1996, 2014 Custom Computer Services          ////
.................... //// This source code may only be used by licensed users of the CCS C  ////
.................... //// compiler.  This source code may only be distributed to other      ////
.................... //// licensed users of the CCS C compiler.  No other use, reproduction ////
.................... //// or distribution is permitted without written permission.          ////
.................... //// Derivative programs created using this software in object code    ////
.................... //// form are not restricted in any way.                               ////
.................... ///////////////////////////////////////////////////////////////////////////
.................... #device PIC18F8722
*
00000:  DATA 6E,41
00002:  DATA 70,70
00004:  DATA 6C,69
00006:  DATA 63,61
00008:  DATA 74,69
0000A:  DATA 6F,6E
0000C:  DATA 20,56
0000E:  DATA 65,72
00010:  DATA 73,69
00012:  DATA 6F,6E
00014:  DATA 20,31
00016:  DATA 2E,30
00018:  DATA 0A,00
*
00044:  TBLRD*+
00046:  MOVF   FF5,F
00048:  BZ    006C
0004A:  MOVFF  FF6,08
0004E:  MOVFF  FF7,09
00052:  MOVFF  FF8,0A
00056:  MOVF   FF5,W
00058:  BTFSS  F9E.4
0005A:  BRA    0058
0005C:  MOVWF  FAD
0005E:  MOVFF  08,FF6
00062:  MOVFF  09,FF7
00066:  MOVFF  0A,FF8
0006A:  BRA    0044
0006C:  GOTO   0144 (RETURN)


I do expect that the whole code will begin at address 0x400 until it reaches the end address depending on the size of the application firmware. Based from the build definition, the start vector of the application is 0x400 while the interrupt vector is from 0x008 to 0x3FF.

Did I miss something here?
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Oct 14, 2017 8:12 am     Reply with quote

You need to have at least one interrupt handler defined.

The code won't automatically locate upward unless the interrupts are being used....

With the interrupts 'unused', it'll use this unused area for storage.
valkyrie.wing



Joined: 12 Oct 2017
Posts: 10

View user's profile Send private message

PostPosted: Sat Oct 14, 2017 10:40 pm     Reply with quote

Hi Ttelmah,

You are right, I need to have #int method defined in order for the #build preprocessor to work. Here's the final code of the application firmware.

Code:

#include <18F8722.h>
#device ADC=16

#FUSES H4                        // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT                 // No code protection from reading
#FUSES NOWDT                     // No Watch Dog Timer
#FUSES NOBROWNOUT                // No brownout reset
#FUSES NOLVP                     // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT                       // power-up timer

#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)

#include "bootloader_op2.h"


#bit TXIF = 0xF9E.4              // clear transmit buffer flag (empty)
#bit RCIF = 0xF9E.5              // clear receive buffer flag (empty)


#define SERIAL_BUFF_SZ           (32)  // Allocated serial comm buffer

byte TXin;                             // transmit data IN index
byte TXout;                            // transmit data OUT index
byte TXbuffer[SERIAL_BUFF_SZ];         // Serial TX buffer


#int_tbe
void serial_tx_isr (void)
{
    // put char to TXbuffer then send
    putc(TXbuffer[TXout]);

    // increment & limit to max buffer size
    TXout = (TXout + 1) % SERIAL_BUFF_SZ;

    // if last in data, stop TX interrupt
    if (TXin == TXout)
    {
        disable_interrupts(int_tbe);   // disable transmit interrupt
    }
   
    return;
}

void serial_bputc (char Ch)
{
    boolean Restart;
    int Next;     

   // set restart flag at start
   Restart = (TXin == TXout);
   
   // store char to TXbuffer
   TXbuffer[TXin] = Ch;
   
   // limit to max buffer size
   Next = (TXin + 1) % SERIAL_BUFF_SZ;

   while (Next == TXout);  // buffer is full
   
   TXin = Next;
   if (Restart == TRUE)    // enable TX interrupt
   {
      enable_interrupts(int_tbe);
   }
   
   return;
}

void main (void)
{
    unsigned int8 Numb = 0;
   
    delay_ms(100);
   
    //-----------------------------------------------------
    // initialize mcu and peripherals
    //-----------------------------------------------------
   
    TXIF = 1;                    // clear transmit buffer flag (empty)
    RCIF = 0;                    // clear receive buffer flag (empty)
   
    enable_interrupts(global);   // enable global interrupt
   
    //-----------------------------------------------------
   
    printf(serial_bputc, "nApplication Version 1.0\n");

    while (TRUE)
    {
        printf(serial_bputc, "%u ", ++Numb);
    }

    return;
}



And here's the final code of the bootloader based from what we had discussed with application boot vector at 0x400 while the interrupt vector is from 0x008 to 0x3FF. The Bootloader boot vector is at 0x000 while the bootloader code is at the end of the program memory.

bootloader_op2.h
Code:

#if !defined(__BOOTLOADER_OP2_H__)
#define __BOOTLOADER_OP2_H__


#define LOADER_END               (getenv("PROGRAM_MEMORY") - 1)

#if (getenv("PROGRAM_MEMORY") % getenv("FLASH_ERASE_SIZE")) == 0
#define LOADER_SIZE              (0x4FF)
#else
#define LOADER_SIZE              (getenv("PROGRAM_MEMORY") % (getenv("FLASH_ERASE_SIZE")) - 1)
#endif

#define LOADER_ADDR              (LOADER_END - LOADER_SIZE)

#define APP_INT_VECTOR_START     (0x008)
#define APP_INT_VECTOR_END       (0x3FF)
#define APP_BOOT_VECTOR          (APP_INT_VECTOR_END + 0x1)

#ifndef _BOOTLOADER_
#build (reset=APP_BOOT_VECTOR, interrupt=APP_INT_VECTOR_START:APP_INT_VECTOR_END)
#org LOADER_ADDR, LOADER_END {}
#else
#org LOADER_ADDR, LOADER_END default
#endif


#endif



bootloader_op2.c
Code:

#include <18F8722.h>
#device ADC=16

#FUSES H4                        // High-speed crystal clock x4 multiplier
#FUSES NOPROTECT                 // No code protection from reading
#FUSES NOWDT                     // No Watch Dog Timer
#FUSES NOBROWNOUT                // No brownout reset
#FUSES NOLVP                     // No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES PUT                       // power-up timer


#define BOOTLOADER_RUN          (PIN_A1)
#define BOOTLOADER_LED          (PIN_A2)

#define ACKLOD                  (0x06)
#define XON                     (0x11)
#define XOFF                    (0x13)

#define BUFFER_LEN_LOD          (64)

#define _BOOTLOADER_
#include "bootloader_op2.h"


#use delay(crystal=40000000)
#use rs232(baud=115200, xmit=PIN_C6, rcv=PIN_C7, parity=N, bits=8, errors)


unsigned int atoi_b16 (char *BuffPtr)
{
    // Convert two hex characters to a int8
    unsigned int Result = 0;
    int Idx;

    for (Idx = 0;
         Idx < 2;
         Idx++, BuffPtr++) 
    {
        if (*BuffPtr >= 'A')
        {
            Result = 16 * Result + (*BuffPtr) - 'A' + 10;
        }
        else
        {
            Result = 16 * Result + (*BuffPtr) - '0';
        }
   }

   return Result;
}

void real_load_program ()
{
    int  BuffIdx;
    char Buffer[BUFFER_LEN_LOD];
   
    int1  DoAcklod, Done = FALSE;
    int8  Checksum, LineType;
    int16 Laddr, Haddr = 0;
    int32 Addr;

    #if getenv("FLASH_ERASE_SIZE") > 2
    int32 NextAddr;
    #endif

    int8  DataIdx, Idx, Count;
    int8  Data[32];

    while (Done == FALSE)       // Loop until the entire program is downloaded
    {
        // Read into the buffer until 0x0D ('\r')
        // is received or the buffer is full
        BuffIdx = 0;
        do
        {
            if (BuffIdx >= BUFFER_LEN_LOD)
            {
                BuffIdx = 0;
            }
           
            Buffer[BuffIdx] = getc();
            if (Buffer[BuffIdx] == ':')
            {
                Buffer[0] = Buffer[BuffIdx];
                BuffIdx = 0;
            }
        } while (    (Buffer[BuffIdx++] != 0x0D)
                  && (BuffIdx <= BUFFER_LEN_LOD));

        putchar(XOFF);  // Suspend sender
        DoAcklod = TRUE;

        // Only process data blocks that start with ':'
        if (Buffer[0] == ':')
        {
            // Get the number of bytes from the buffer
            Count = atoi_b16(&Buffer[1]);
            // Get the lower 16 bits of address
            Laddr = make16(atoi_b16(&Buffer[3]), atoi_b16(&Buffer[5]));
            // Get the line type
            LineType = atoi_b16(&Buffer[7]);
            // Get the program memory address
            Addr = make32(Haddr, Laddr);

            // If the line type is 1, then data is done being sent
            if (LineType == 1)
            {
                Done = TRUE;
            }
            else if (    (    Addr < LOADER_ADDR
                           || Addr > LOADER_END)
                      && Addr < 0x300000)
            {
                Checksum = 0;   // Sum the bytes to find the check sum value
                for (Idx = 1;
                     Idx < (BuffIdx - 3);
                     Idx += 2)
                {
                    Checksum += atoi_b16(&Buffer[Idx]);
                }

                Checksum = 0xFF - Checksum + 1;
                if (Checksum != atoi_b16(&Buffer[BuffIdx - 3]))
                {
                    DoAcklod = FALSE;
                }
                else
                {
                    if (LineType == 0)
                    {
                        // Loops through all of the data and stores it in data
                        // The last 2 bytes are the check sum, hence buffidx-3
                        for (Idx = 9, DataIdx = 0;
                             Idx < (BuffIdx - 3);
                             Idx += 2)
                        {
                            Data[DataIdx++] = atoi_b16(&Buffer[Idx]);
                        }

                        #if getenv("FLASH_ERASE_SIZE") > 2
                        if (    (Addr != NextAddr)
                             && (Addr & (getenv("FLASH_ERASE_SIZE") / 2 - 1) != 0))
                        {
                            erase_program_eeprom(addr);
                        }
                       
                        NextAddr = Addr + 1;
                        #endif

                        write_program_memory(Addr, Data, Count);
                    }
                    else if (LineType == 4)
                    {
                        Haddr = make16(atoi_b16(&Buffer[9]), atoi_b16(&Buffer[11]));
                    }
                    else
                    {
                        ;;;;  // just do nothing here
                    }
                }   // else
            }   // else if
            else
            {
                ;;;;  // just do nothing here
            }
        }   // end if

        if (DoAcklod == TRUE)
        {
            putchar(ACKLOD);
        }
       
        putchar(XON);
    }   // end while

    putchar(ACKLOD);
    putchar(XON);
    return;
}

void main (void)
{
    setup_adc_ports(NO_ANALOGS);                // setup PortA to digital I/O
   
    if (input(BOOTLOADER_RUN) == TRUE)          // firmware upgrade button is enabled
    {
        output_high(BOOTLOADER_LED);            // turn-ON bootloader LED
       
        // write new firmware to program memory
        real_load_program();                   
        output_low(BOOTLOADER_LED);             // turn-OFF bootloader LED
    }
   
    // run the application firmware
    #asm
    GOTO APP_BOOT_VECTOR
    NOP
    NOP
    #endasm
   
   return;
}



BTW, the jump instruction is using GOTO 0x400 instead of CALL to run the application firmware after the bootloader code is executed either for firmware update or not.

Thanks once again for the help.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sun Oct 15, 2017 12:09 am     Reply with quote

Well done.

When you posted the earlier code I suddenly 'remembered' that it doesn't reserve the interrupt area if no interrupt is used. Had met this myself ages ago when trying to do something similar....

Yes, goto makes sense. There is nothing to 'return' to.

Hopefully you will find it a nice reliable solution. Smile
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