Monday, September 1, 2014

Bootloader concept

Our CAN bus will work between 20 to max 100k baud rate. This limits me between 180 to max 900 CAN messages per second (given that each CAN message contains 8 bytes).

Any CAN-message consists of 47 bits overhead (including 3 bits 'Interframe Space') plus 0 to 8 bytes data (not taking into account the possibility of extra stuff-bits, which will increase the number of bits slightly). 

The used processor PIC24EP128 has 128kb of flash. In worst case, the entire flash has to be transmitted over the CAN bus for the firmware update. This means 88042 addresses of each 3 bytes will have to be transmitted. If we also include the flash address destination in each message, than again this would require 3 bytes.

With CAN, you can send 8 bytes maximum for each message. If we arrange it with 3 bytes FLASH address destination and 3 bytes FLASH data for destination, this means we can at most do 1 address for each can message thus we need to send 88042 CAN messages if the entire FLASH space is used.

With the maximum CAN bus messages per seconds that I've shown in the start, this leads to a firmware upload time between 489 seconds (8minutes) to 97 seconds. Of course, there are some optimizations reachable to reduce this.

Comm spec.
Each bracked equals a byte, max 8 byte for a CAN msg.

If mode = 255

[MODE=255][ADDR2][ADDR1][ADDR0][DATy2][DATy1][DATy0]

If mode = 254

In this mode the address will be automatically incremented. And each CAN frame will consist data for two FLASH data addresses.

[MODE=254][DATx2][DATx1][DATx0][DATy2][DATy1][DATy0]

Combining the above two methods almost reduces the time with 50%. Leaving 4 minutes as worst case scenario as wait time. For my project, this is acceptable.

If mode =  253

When all the firmware data has been transmitted, the PC will send a mode = 3 message with as data the start and end address of the FLASH memory.

[MODE=253][START_ADDR2][ADDR1][ADDR0][END_ADDR2][ADDR1][ADDR0]

This will request a CRC16 from the updated node. The CRC16 will be made up from the written data in the flash, not the received data. This is to prevent the possibility that there was a write error into the flash and to make sure that everything went fine.

If mode =  252

Erases the flash DST from start to end.

[MODE=252][START_ADDR2][ADDR1][ADDR0][END_ADDR2][ADDR1][ADDR0]

If mode =  251

Reads the flash DST and respond this on the CANbus network as a RTR

PC -> Embedded
[MODE=251][ADDR2][ADDR1][ADDR0]

Embedded -> PC
[MODE=251][ADDR2][ADDR1][ADDR0][DATy2][DATy1][DATy0]












Bootloader for PIC24E

I`m making a bootloader for the PIC24E device. The device I use is an PIC24EP128GP004. The idea is that I will place the bootloader at the end of the flash area, not at the beginning. This way, I avoid remapping all of the interrupt sources and thus very little changes I've to make to the *.GLD file.

The PIC24EP I use, is equipped with two CAN bus controllers on my PCB (So NO on-board CAN). From one of this external CAN controllers, the bootloader must be able to communicate and receive new firmware if available. The CAN bus controller is of the type MCP2515.

About the micro controller

The FLASH area addressing starts from 0x0 to 0x015800 for the 128kb device. This means the total available pages on this device is 42 (1024*3*42=129024 bytes). Please note that these 24EP devices have 0x400 instructions in a single page, not 0x200 like the 24FJ series. This means 3072 bytes or 3kb for a single page for the PIC24EP series.

I will reserve 2 pages for the bootloader. So I will use the following layout;
  • Page 0 to 39 dedicated for user application
  • Page 40 and 41 dedicated for bootloader
  • Page 42 dedicated for configuration fuses
By dedicating the configuration fuses, it will still be possible for the bootloader to change the configuration fuses if needed. e.g. if you want to be able to change the code protection with the bootloader. Please note this is a choice of me, not a requirement of microchip. This choice will come at a price that you will waste 1 entire page since it cannot be used by the bootloader nor user application.

Scratchpad below

Using code:
            nvmAdru = __builtin_tblpage(&myRowData1InFlash[0]);
            nvmAdr = __builtin_tbloffset(&myRowData1InFlash[0]);
            nvmAdrPageAligned = nvmAdr & 0xF800; // Get the Flash Page Aligned address

Declared:

uint16_t myRowData1InFlash[] __attribute__((space(prog), address(0xFC00))) ;

Result

  • nvmAdru = 0x100
  • nvmAdr = 0xFC00
  • nvmAdrPageAligned = 0xF800

Declared

uint16_t myRowData1InFlash[] __attribute__((space(prog), address(0x10400)));

Result
  • nvmAdru = 0x101
  • nvmAdr = 0x400
  • nvmAdrPageAligned = 0x0
Declared

uint16_t myRowData1InFlash[] __attribute__((space(prog), address(0x11200))) ;

Result
  • nvmAdru = 0x101
  • nvmAdr = 0x1200
  • nvmAdrPageAligned = 0x1000
Declared

uint16_t myRowData1InFlash[] __attribute__((space(prog), address(0x200))) = {

  • nvmAdru = 0x100
  • nvmAdr = 0x200
  • nvmAdrPageAligned = 0x0

The microchip PIC24EP simulator, when erasing a page, it only erases 0x100 addresses. In real world test, this is wrong, the same code will erase 0x800 addresses.