Designing bootloader for Microchip dsPIC33E/PIC24E micro-controller(4)

If you have read my previous post about how to extract useful data from INTE HEX32 format hex file, you are just one step away from being able to burn the firmware via bootloader.

Unlike an ICD programmer that burns the firmware through PCG/PCD pins, Microchip has a complete set of instructions / registers that are dedicated to overwrite its own flash for the 16-bit MCU series. For example:

  • NVMADRU (for higher 8-bit address) and NVMADR (for lower 16-bit address) are Flash address registers, pointing to the physical location (24-bit address) of the flash where binary firmware will be programed/erased/read.
  • NVMOP of NVMCON register. Operation select bits to perform bulk erase, page erase, row program and etc., depending on different settings.
  • TBLPAG register is used to store upper 8-bit of 24-bit address for the “Write latches” (Scroll down for detailed description of the Write Latches”). For dsPIC33E or PIC24E device, you should always load “0xFA” into TBLPAG because the “Write latche” is physically implemented from address 0xFA0000.BL4_Write_Latches_Address
  • TBLWTH and TBLWTL instructions are needed to load the address of binary data in the RAM into “Write latches” before the actual burning process.
  • and many other…..

Since Microchip implemented the “row program” scheme, it is more convenient and faster to do bootloading by rows, or every 128 instructions. “Write latches” could be considered as a temporary storage place on the Flash where 128 instructions are held before they get burned into their final destination. In page program mode, Microchip doesn’t seem to support writing into the flash directly from RAM. All data must be firstly loaded into RAM and then transferred to the Write Latches, before finally “burned” into the flash. The following piece of codes is an example:

  1. Receive firmware through peripherals such as UART, SPI, I2C, PMP or IO  and store them in RAM.
            .bss
            buffer:  .space (#128*3)      ;Create a array size of 128*3 bytes in RAM
            ;Your code here to receive binary data via UART/SPI/I2C
    
  2. Load the data from RAM into Write Laches.
    	mov	#128, W1      ;To burn 128 instructions, which is the size of a "row"
            mov       #0xFA, W0
            mov       W0, TBLPAG    ;As explained above, Write Latches starts from 0xFA0000
            mov       #0, W2        ;Lower 16-bit of Write Latches starts from 0
            mov       #buffer, W3   ;Load buffer (RAM) address
    WRLA:   tblwth.b  [W3++], [W2]
            tblwtl.b  [W3++], [W2++]
            tblwtl.b  [W3++], [W2++]
            dec       W1, W1
            bra       nz, WRLA
    
  3. Execute the row program command.
    	;Before starting writing into the flash,
            ;we must know what's the starting address for this 128-instructions
            ;Assume upper byte of the address is already loaded into W0
            mov       W0, NVMADRU    ;Load the upper byte of address into NVMADRU register
            ;Assume mid/lower bytes of the address are already loaded into W0
            mov       W0, NVMADR     ;Load the mid/lower bytes of the address into NVMADR register
            ;Now the MCU knows where the data are (Write Latches) and where they should go (loaded
            ;in NVMADRU and NVMADR registers)
            ;We are ready to starting the "burning" process!
            mov       #0x4002, W0
            mov       W0, NVMCON
            mov       #0x55, W0
            mov       W0, NVMKEY
            mov       #0xAA, W0
            mov       W0, NVMKEY
            bset      NVMCON, #WR
            nop
            nop
            ;Wait for write to finish
    WWTF:   btsc    NVMCON, #WR
            bra     WWTF
    
  4. Now you have just finished one row of firmware. Repeat from step 1 to step 3 until all the firmware is burned into your flash.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.