PIC18 Bootloader.asm
PIC18 Bootloader.asm
;
; Microchip licenses this software to you solely for use with Microchip
; products. The software is owned by Microchip and its licensors, and
; is protected under applicable copyright laws. All rights reserved.
;
; SOFTWARE IS PROVIDED "AS IS." MICROCHIP EXPRESSLY DISCLAIMS ANY
; WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT
; NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
; FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL
; MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
; CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR
; EQUIPMENT, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY
; OR SERVICES, ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED
; TO ANY DEFENSE THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION,
; OR OTHER SIMILAR COSTS.
;
; To the fullest extent allowed by law, Microchip and its licensors
; liability shall not exceed the amount of fees, if any, that you
; have paid directly to Microchip to use this software.
;
; MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE
; OF THESE TERMS.
;
; Author Date Comment
; ************************************************************************
; E. Schlunder 07/20/2010 Software Boot Block Write Protect code
; improved. 96KB memory size devices should
; work now.
; E. Schlunder 02/26/2010 Changed order of start up code so that PLLEN
; is enabled before we wait for RXD IDLE state.
; This improves connection time/reliablity on
; J devices.
; E. Schlunder 08/28/2009 Software Boot Block Write Protect option.
; E. Schlunder 07/09/2009 Brought back support for bootloader at
; address 0 for hardware boot block write
; protection on certain devices.
; E. Schlunder 05/07/2009 Replaced the simple checksum with a
; 16-bit CCIT CRC checksum.
; Added ReadFlashCrc command for quick verify.
; E. Schlunder 05/02/2009 Improved autobaud code to handle 1Mbps
; and BRG16/BRGH.
; E. Schlunder 05/01/2009 Added support for DEVICES.INC generated
; from Device Database tool.
; E. Schlunder 04/29/2009 Added support for locating the bootloader
; at the end of program memory instead of
; the beginning. This will eventually let us
; use normal application firmware code without
; linker script modifications.
; E. Schlunder 04/26/2009 Optimized Config Write routine to avoid
; re-writing values matching existing config
; data.
; E. Schlunder 04/24/2009 Optimized EEPROM Write routine a little bit.
; Optimized FLASH Read routine to stream data
; directly from FLASH instead of using RAM
; buffer.
; Added option for faster STX acknowledgements.
; E. Schlunder 04/17/2009 Added code to enter bootloader mode if
; a serial break condition is detected on
; RXD as we come out of reset. This will
; make it possible to re-enter the bootloader
; even if the application firmware is missing
; code to re-enter bootloader mode.
; This also simplies the bootloader, as
; we do not need a boot flag any more.
; E. Schlunder 04/15/2009 Removed EOF command 8, new PC software
; does 64 byte block aligned writes at all
; times on J device, so there is no need for
; this command going forward.
; E. Schlunder 04/14/2009 Added a BootloadMode vector back at the
; beginning of program memory so that user
; applications can jump back into the boot
; loader without having to erase the boot flag.
; E. Schlunder 04/08/2009 Now initializes FSR2 to 0 so that the code
; can operate under Extended Instruction Set
; mode if necessary.
; E. Schlunder 04/01/2009 Fixed bug in J_FLASH erase address increment.
; Added support for enabling PLL.
; Added support for inverted UART signaling.
; Added support for fixed (non-autobaud)
; operation, helps with debugging code under ICD.
; E. Schlunder 03/25/2009 No longer attempts to use EEADRH on PIC18F4321.
;
; UART Bootloader for PIC18F by Ross Fosler
; 09/01/2006 Modified to support PIC18xxJxx & 160k PIC18Fxxx Flash Devices
; 03/01/2002 ... First full implementation
; 03/07/2002 Changed entry method to use last byte of EEDATA.
; Also removed all possible infinite loops w/ clrwdt.
; 03/07/2002 Added code for direct boot entry. I.E. boot vector.
; 03/09/2002 Changed the general packet format, removed the LEN field.
; 03/09/2002 Modified the erase command to do multiple row erase.
; 03/12/2002 Fixed write problem to CONFIG area. Write was offset by a byte.
; 03/15/2002 Added work around for 18F8720 tblwt*+ problem.
; 03/20/2002 Modified receive & parse engine to vector to autobaud on a checksum
; error since a chechsum error could likely be a communications problem.
; 03/22/2002 Removed clrwdt from the startup. This instruction affects the TO and
; PD flags. Removing this instruction should have no affect on code
; operation since the wdt is cleared on a reset and boot entry is always
; on a reset.
; 03/22/2002 Modified the protocol to incorporate the autobaud as part of the
; first received <STX>. Doing this improves robustness by allowing
; re-sync under any condition. Previously it was possible to enter a
; state where only a hard reset would allow re-syncing.
; 03/27/2002 Removed the boot vector and related code. This could lead to
customer
; issues. There is a very minute probability that errent code execution
; could jump into the boot area and cause artificial boot entry.
; *****************************************************************************
#include <p18cxxx.inc>
#include "devices.inc"
#include "bootconfig.inc"
#include "preprocess.inc"
; *****************************************************************************
; hecho para un PIC18F4620 18/06/2011
;**********************************************************************************
; Bit de configuraci�n. La directiva CONFIG define los valores de los bit de
; configuraci�n desde un archivo en ensamblador ( nombre.asm )
; Las etiquetas de la directivas est�n definidas en el archivo P18F4620.INC
;
; CONFIG1H 300001h IESO, FCMEN, FOSC<3:0>
; CONFIG2L 300002h BORV<1:0>, BOREN<1:0>, PWRTEN
; CONFIG2H 300003h WDTPS<3:0>, WDTEN
; -------- 300004h
; CONFIG3H 300005h MCLRE, LTP1OSC, PBADEN
; CONFIG4L 300006h DEBUG, XINST, LVP, STVREN
; -------- 300007h
; CONFIG5L 300008h CP<3:0>
; CONFIG5H 300009h CPD, CPB
; CONFIG6L 30000Ah WRT<3:0>
; CONFIG6H 30000Bh WRTD, WRTB, WRTC
; CONFIG7L 30000Ch EBTR<3:0>
; CONFIG7H 30000Dh EBTRB
;**********************************************************************************
; Valores definidos para la operaci�n con el cargador de programas
;**********************************************************************************
; CONFIG1H (300001h) => Definido la operaci�n con un cristal externo habilitando
; el funcionamiento del PLL ( SYSCLK = 4 x FOSC )
; => Deshabilitado chequeo del funcionamiento del
oscilador
; => Deshabilitado cambio por software del SYSCLK
;
CONFIG OSC = HSPLL, FCMEN = ON, IESO = OFF
; CONFIG = 0x46
;
; CONFIG2L (300002h) => Habilitado el control "Brown-OutReset" por software
mediante
; el manejo del bit SBOREN
; => Voltaje de operaci�n del "Brown-OutReset" de
4.6v
; => Deshabilitado temporizador de arranque de la
fuente
;
CONFIG BOREN = ON, BORV = 0, PWRT = OFF
; CONFIG = 0x03
;
; CONFIG2H (300003h) => Deshabilitado el control del "Watch Dog Timer" por hardware
; permitiendo el control por software mediante el
bit SWDTEN
; => El escalador de tiempo fijado en 1:32768
;
CONFIG WDT = OFF, WDTPS = 32768
; CONFIG = 0x1E
;
; CONFIG3H (300005h) => El puerto RB<4:0> operan como digital despu�s del RESET
; => El pin MCLRE habilitado
; => Oscilador timer 1 deshabilitado
;
CONFIG MCLRE = ON, PBADEN = OFF, LPT1OSC = OFF
; CONFIG = 0x81
;
; CONFIG4L (300006h) => Deshabilitado el debuger por hardware
; => Definido tama�o del bloque de arranque en 1 KWord
( 0000-07FF )
; => Deshabilitado la programaci�n con bajo voltaje
; => Deshabilitado la ejecuci�n instrucciones
extendidas
; => Habilitado el reset por overflow de la pila
( STACK )
;
CONFIG DEBUG = OFF, XINST = OFF, LVP = OFF, STVREN = ON
; CONFIG = 0x81
;
; CONFIG5H (300008h) => Deshabilitada protecci�n lectura memoria FLASH (PROGRAMA)
;
CONFIG CP0 = OFF, CP1 = OFF, CP2 = OFF, CP3 = OFF
; CONFIG = 0x0F
;
; CONFIG5H (300009h) => Deshabilitado protecci�n escritura bloque de arranque
; => Deshabilitada protecci�n escitura memoria eeprom
;
CONFIG CPB = OFF, CPD = OFF
; CONFIG = 0xC0
;
; CONFIG5H (30000Ah) => Deshabilitada protecci�n esritura memoria FLASH (PROGRAMA)
;
CONFIG WRT0 = OFF, WRT1 = OFF, WRT2 = OFF, WRT3 = OFF
; CONFIG = 0x0F
;
; CONFIG6H (30000Bh) => Deshabilitado protecci�n contra escritura del bloque de
arranque
; => Habiliatda protecci�n contra escritura de
registros configuraci�n
; => Deshabilitado la protecci�n de escritura para
la memoria eeprom
;
CONFIG WRTB = OFF, WRTC = ON, WRTD = OFF
; CONFIG = 0xC0
;
; CONFIG5H (30000Ch) => Deshabilitada protecci�n lectura memoria FLASH (PROGRAMA)
;
CONFIG EBTR0 = OFF, EBTR1 = OFF, EBTR2 = OFF, EBTR3 = OFF
; CONFIG = 0x0F
;
; CONFIG7H (30000Dh) => Permitida la lectura del bloque de arranque
;
CONFIG EBTRB = OFF
; CONFIG = 0x40
; *****************************************************************************
#define STX 0x0F
#define ETX 0x04
#define DLE 0x05
#define NTX 0xFF
; *****************************************************************************
; *****************************************************************************
; RAM Address Map
CRCL equ 0x00
CRCH equ 0x01
RXDATA equ 0x02
TXDATA equ 0x03
; *****************************************************************************
errorlevel -311 ; don't warn on HIGH() operator values >16-bits
#ifdef USE_SOFTBOOTWP
#ifndef SOFTWP
#define SOFTWP
#endif
#endif
#ifdef USE_SOFTCONFIGWP
#ifdef CONFIG_AS_FLASH
#ifndef SOFTWP
#define SOFTWP
#endif
#endif
#endif
#ifndef AppVector
; The application startup GOTO instruction will be written just before the Boot
Block,
; courtesy of the host PC bootloader application.
#define AppVector (BootloaderStart-.4)
#endif
; *****************************************************************************
; *****************************************************************************
#if BOOTLOADER_ADDRESS != 0
ORG 0
; The following GOTO is not strictly necessary, but may startup faster
; for large microcontrollers running at extremely slow clock speeds.
;goto BootloaderBreakCheck
ORG BOOTLOADER_ADDRESS
BootloaderStart:
bra BootloadMode
; *****************************************************************************
; Determine if the application is supposed to be started or if we should
; go into bootloader mode.
;
; If RX pin is in BREAK state when we come out of MCLR reset, immediately
; enter bootloader mode, even if there exists some application firmware in
; program memory.
BootloaderBreakCheck:
DigitalInput ; set RX pin as digital input on certain parts
#ifdef INVERT_UART
btfss RXPORT, RXPIN
GotoAppVector:
goto AppVector ; no BREAK state, attempt to start application
#else
btfsc RXPORT, RXPIN
GotoAppVector:
goto AppVector ; no BREAK state, attempt to start application
#endif
BootloadMode:
DigitalInput ; set RX pin as digital input on certain parts
#else ; BOOTLOADER_ADDRESS == 0
****************************************************************
ORG 0
BootloaderStart:
DigitalInput ; set RX pin as digital input on certain parts
movlw low(AppVector) ; load address of application reset vector
bra BootloaderBreakCheck
ORG 0x0008
HighPriorityInterruptVector:
goto AppHighIntVector ; Re-map Interrupt vector
BootloaderBreakCheck:
#ifdef INVERT_UART
btfsc RXPORT, RXPIN
bra BootloadMode
#else
btfss RXPORT, RXPIN
bra BootloadMode
#endif
CheckAppVector:
; Read instruction at the application reset vector location.
; If we read 0xFFFF, assume that the application firmware has
; not been programmed yet, so don't try going into application mode.
movwf TBLPTRL
movlw high(AppVector)
movwf TBLPTRH
bra CheckAppVector2
ORG 0x0018
LowPriorityInterruptVector:
goto AppLowIntVector ; Re-map Interrupt vector
CheckAppVector2:
movlw upper(AppVector)
movwf TBLPTRU
tblrd *+ ; read instruction from program memory
incfsz TABLAT, W ; if the lower byte != 0xFF,
GotoAppVector:
goto AppVector ; run application.
#ifdef USE_PLL
#ifdef PLLEN
#ifdef OSCTUNE
bsf OSCTUNE, PLLEN ; enable PLL for faster internal clock
#else
; 18F8680, 18F8585, 18F6680, and 18F6585 doesn't have OSCTUNE register.
; Instead, PLLEN bit is in OSCCON.
bsf OSCCON, PLLEN ; enable PLL for faster internal clock
#endif
#else
#ifdef SPLLEN
bsf OSCTUNE, SPLLEN ; PIC18F14K50 has SPLLEN at bit 6
#endif
#endif
#endif
#ifdef INVERT_UART
btfsc RXPORT, RXPIN ; wait for RX pin to go IDLE
bra $-2
#else
btfss RXPORT, RXPIN ; wait for RX pin to go IDLE
bra $-2
#endif
#ifdef PPS_UTX_PIN
banksel PPSCON
; unlock PPS registers
movlw 0x55
movwf EECON2, ACCESS
movlw 0xAA
movwf EECON2, ACCESS
bcf PPSCON, IOLOCK, BANKED
movlw PPS_URX_PIN
movwf PPS_URX, BANKED
#ifdef BRG16
bsf UxBAUDCON, BRG16
movlw b'00000010' ; 1:8 prescaler - no division required later (but
no rounding possible)
#else
movlw b'00000011' ; 1:16 prescaler - thus we only have to divide by 2
later on.
#endif
movwf T0CON
#ifdef PICDEM_LCD2
bsf LATB, LATB0 ; PICDEM LCD 2 demoboard requires RB0 high to
enable MAX3221 TX output to PC.
bcf TRISB, TRISB0
#endif
; *****************************************************************************
; *****************************************************************************
#ifdef USE_AUTOBAUD
DoAutoBaud:
; ___ __________ ________
; \__/ \__________/
; | |
; |-------- p ----------|
;
; p = The number of instructions between the first and last
; rising edge of the RS232 control sequence 0x0F. Other
; possible control sequences are 0x01, 0x03, 0x07, 0x1F,
; 0x3F, 0x7F.
;
; SPBRG = (p / 32) - 1 BRGH = 1, BRG16 = 0
; SPBRG = (p / 8) - 1 BRGH = 1, BRG16 = 1
RetryAutoBaud:
clrf TMR0H ; reset timer count value
clrf TMR0L
bcf INTCON, TMR0IF
rcall WaitForRise ; wait for a start bit to pass by
bsf T0CON, TMR0ON ; start timer counting for entire D7..D0 data bit
period.
rcall WaitForRise ; wait for stop bit
bcf T0CON, TMR0ON ; stop the timer from counting further.
btfsc INTCON, TMR0IF ; if TMR0 overflowed, we did not get a good baud
capture
bra RetryAutoBaud ; try again
#ifdef BRG16
; save new baud rate generator value
movff TMR0L, UxSPBRG ; warning: must read TMR0L before TMR0H holds real
data
movff TMR0H, UxSPBRGH
#else
movff TMR0L, UxSPBRG ; warning: must read TMR0L before TMR0H holds real
data
; TMR0H:TMR0L holds (p / 16).
rrcf TMR0H, w ; divide by 2
rrcf UxSPBRG, F
btfss STATUS, C ; rounding
decf UxSPBRG, F
#endif
WaitForHostCommand:
rcall ReadHostByte ; get start of transmission <STX>
xorlw STX
bnz DoAutoBaud ; got something unexpected, perform autobaud
#else ; not using autobaud
movlw low(BAUDRG) ; set fixed baud rate generator value
movwf UxSPBRG
#ifdef UxSPBRGH
#if high(BAUDRG) != 0
movlw high(BAUDRG)
movwf UxSPBRGH
#endif
#endif
bsf UxRCSTA, CREN ; start receiving
DoAutoBaud:
WaitForHostCommand:
rcall ReadHostByte ; get start of transmission <STX>
xorlw STX
bnz WaitForHostCommand ; got something unexpected, keep waiting for <STX>
#endif ; end #ifdef USE_AUTOBAUD
; *****************************************************************************
; *****************************************************************************
; Read and parse packet data.
StartOfLine:
movlw STX ; send back start of response
rcall SendHostByte
ReceiveDataLoop:
rcall ReadHostByte ; Get the data
xorlw STX ; Check for an unexpected STX
bz StartOfLine ; unexpected STX: abort packet and start over.
NoSTX:
movf RXDATA, W
xorlw ETX ; Check for a ETX
bz VerifyPacketCRC ; Yes, verify CRC
NoETX:
movf RXDATA, W
xorlw DLE ; Check for a DLE
bnz AppendDataBuffer
rcall ReadHostByte ; DLE received, get the next byte and store it
AppendDataBuffer:
movff RXDATA, PREINC0 ; store the data to the buffer
bra ReceiveDataLoop
VerifyPacketCRC:
lfsr FSR1, COMMAND
clrf CRCL
clrf CRCH
movff POSTDEC0, PRODH ; Save host packet's CRCH to PRODH for later
comparison
; CRCL is now available as INDF0
VerifyPacketCrcLoop:
movf POSTINC1, w
rcall AddCrc ; add new data to the CRC
movf FSR1H, w
cpfseq FSR0H
bra VerifyPacketCrcLoop ; we aren't at the end of the received data
yet, loop
movf FSR1L, w
cpfseq FSR0L
bra VerifyPacketCrcLoop ; we aren't at the end of the received data
yet, loop
movf CRCH, w
cpfseq PRODH
bra DoAutoBaud ; invalid CRC, reset baud rate generator to re-
sync with host
movf CRCL, w
cpfseq INDF0
bra DoAutoBaud ; invalid CRC, reset baud rate generator to re-
sync with host
; ***********************************************
; Pre-setup, common to all commands.
clrf CRCL
clrf CRCH
; ***********************************************
; Test the command field and sub-command.
CheckCommand:
movlw .10
cpfslt COMMAND
bra DoAutoBaud ; invalid command - reset baud generator to re-sync
with host
; This jump table must exist entirely within one 256 byte block of program
memory.
#if ($ & 0xFF) > (0xFF - .24)
; Too close to the end of a 256 byte boundary, push address forward to get code
; into the next 256 byte block.
messg "Wasting some code space to ensure jump table is aligned."
ORG $+(0x100 - ($ & 0xFF))
#endif
JUMPTABLE_BEGIN:
movf PCL, w ; 0 do a read of PCL to set PCLATU:PCLATH to
current program counter.
rlncf COMMAND, W ; 2 multiply COMMAND by 2 (each BRA instruction
takes 2 bytes on PIC18)
addwf PCL, F ; 4 Jump in command jump table based on COMMAND
from host
bra BootloaderInfo ; 6 00h
bra ReadFlash ; 8 01h
bra VerifyFlash ; 10 02h
bra EraseFlash ; 12 03h
bra WriteFlash ; 14 04h
bra ReadEeprom ; 16 05h
bra WriteEeprom ; 18 06h
bra WriteConfig ; 20 07h
bra GotoAppVector ; 22 08h
reset ; 24 09h
#ifdef INVERT_UART
WaitForRise:
clrwdt
WaitForRiseLoop:
btfsc INTCON, TMR0IF ; if TMR0 overflowed, we did not get a good baud
capture
return ; abort
WtSR:
btfsc RXPORT, RXPIN ; Wait for starting edge
bra WtSR
return
#else ; not inverted UART pins
WaitForRise:
clrwdt
WaitForRiseLoop
btfsc INTCON, TMR0IF ; if TMR0 overflowed, we did not get a good baud
capture
return ; abort
WtSR:
btfss RXPORT, RXPIN ; Wait for rising edge
bra WtSR
return
#endif ; end #ifdef INVERT_UART
; *****************************************************************************
; ***********************************************
; Commands
; ***********************************************
; In: <STX>[<0x00>]<CRCL><CRCH><ETX>
; Out:
<STX><BOOTBYTESL><BOOTBYTESH><VERL><VERH><STARTBOOTL><STARTBOOTH><STARTBOOTU><0x00>
<CRCL><CRCH><ETX>
BootloaderInfo:
movlw low(BootInfoBlock)
movwf TBLPTRL
movlw high(BootInfoBlock)
movwf TBLPTRH
movlw upper(BootInfoBlock)
movwf TBLPTRU
movlw (BootInfoBlockEnd - BootInfoBlock)
movwf DATA_COUNTL
clrf DATA_COUNTH
;; fall through to ReadFlash code -- send Bootloader Information Block from
FLASH.
; In: <STX>[<0x01><ADDRL><ADDRH><ADDRU><0x00><BYTESL><BYTESH>]<CRCL><CRCH><ETX>
; Out: <STX>[<DATA>...]<CRCL><CRCH><ETX>
ReadFlash:
tblrd *+ ; read from FLASH memory into TABLAT
movf TABLAT, w
rcall SendEscapeByte
rcall AddCrc
; In: <STX>[<0x02><ADDRL><ADDRH><ADDRU><0x00><BLOCKSL><BLOCKSH>]<CRCL><CRCH><ETX>
; Out: <STX>[<CRCL1><CRCH1>...<CRCLn><CRCHn>]<ETX>
VerifyFlash:
tblrd *+
movf TABLAT, w
rcall AddCrc
movf CRCL, w
call SendEscapeByte
movf CRCH, w
call SendEscapeByte
#ifdef SOFTWP
reset ; this code -should- never be executed, but
reset ; just in case of errant execution or buggy
reset ; firmware, these reset instructions may protect
reset ; against accidental erases.
#endif
; In: <STX>[<0x03><ADDRL><ADDRH><ADDRU><0x00><PAGESL>]<CRCL><CRCH><ETX>
; Out: <STX>[<0x03>]<CRCL><CRCH><ETX>
EraseFlash:
#ifdef SOFTWP
#define ERASE_ADDRESS_MASK (~(ERASE_FLASH_BLOCKSIZE-1))
#if upper(ERASE_ADDRESS_MASK) != 0xFF
movlw upper(ERASE_ADDRESS_MASK) ; force starting address to land on a
FLASH Erase Block boundary
andwf TBLPTRU, f
#endif
#if high(ERASE_ADDRESS_MASK) != 0xFF
movlw high(ERASE_ADDRESS_MASK) ; force starting address to land on a FLASH
Erase Block boundary
andwf TBLPTRH, f
#endif
#if low(ERASE_ADDRESS_MASK) != 0xFF
movlw low(ERASE_ADDRESS_MASK) ; force starting address to land on a FLASH
Erase Block boundary
andwf TBLPTRL, f
#endif
; Verify Erase Address does not attempt to erase beyond the end of FLASH memory
movlw low(END_FLASH)
subwf TBLPTRL, w
movlw high(END_FLASH)
subwfb TBLPTRH, w
movlw upper(END_FLASH)
subwfb TBLPTRU, w
bn EraseEndFlashAddressOkay
EraseEndFlashAddressOkay:
#ifdef USE_SOFTCONFIGWP
#ifdef CONFIG_AS_FLASH
movlw low(END_FLASH - ERASE_FLASH_BLOCKSIZE)
subwf TBLPTRL, w
movlw high(END_FLASH - ERASE_FLASH_BLOCKSIZE)
subwfb TBLPTRH, w
movlw upper(END_FLASH - ERASE_FLASH_BLOCKSIZE)
subwfb TBLPTRU, w
bn EraseConfigAddressOkay
EraseConfigAddressOkay:
#endif ; end CONFIG_AS_FLASH
#endif ; end USE_SOFTCONFIGWP
#ifdef USE_SOFTBOOTWP
movlw low(BOOTLOADER_ADDRESS)
subwf TBLPTRL, w
movlw high(BOOTLOADER_ADDRESS)
subwfb TBLPTRH, w
movlw upper(BOOTLOADER_ADDRESS)
subwfb TBLPTRU, w
bn EraseAddressOkay
EraseAddressOkay:
#ifdef EEADR
movlw b'10010100' ; setup FLASH erase
#else
movlw b'00010100' ; setup FLASH erase for J device (no EEPROM bit)
#endif
movwf EECON1
NextEraseBlock:
; Decrement address by erase block size
#if ERASE_FLASH_BLOCKSIZE >= .256
movlw high(ERASE_FLASH_BLOCKSIZE)
subwf TBLPTRH, F
clrf WREG
subwfb TBLPTRU, F
#else
movlw ERASE_FLASH_BLOCKSIZE
subwf TBLPTRL, F
clrf WREG
subwfb TBLPTRH, F
subwfb TBLPTRU, F
#endif
decfsz DATA_COUNTL, F
bra EraseFlash
bra SendAcknowledge ; All done, send acknowledgement packet
#ifdef SOFTWP
reset ; this code -should- never be executed, but
reset ; just in case of errant execution or buggy
reset ; firmware, these reset instructions may protect
reset ; against accidental writes.
#endif
; In: <STX>[<0x04><ADDRL><ADDRH><ADDRU><0x00><BLOCKSL><DATA>...]<CRCL><CRCH><ETX>
; Out: <STX>[<0x04>]<CRCL><CRCH><ETX>
WriteFlash:
#ifdef SOFTWP
#define WRITE_ADDRESS_MASK (~(WRITE_FLASH_BLOCKSIZE-1))
#if upper(WRITE_ADDRESS_MASK) != 0xFF
movlw upper(WRITE_ADDRESS_MASK) ; force starting address to land on a
FLASH Write Block boundary
andwf TBLPTRU, f
#endif
#if high(WRITE_ADDRESS_MASK) != 0xFF
movlw high(WRITE_ADDRESS_MASK) ; force starting address to land on a FLASH
Write Block boundary
andwf TBLPTRH, f
#endif
#if low(WRITE_ADDRESS_MASK) != 0xFF
movlw low(WRITE_ADDRESS_MASK) ; force starting address to land on a FLASH
Write Block boundary
andwf TBLPTRL, f
#endif
; Verify Write Address does not attempt to write beyond the end of FLASH memory
movlw low(END_FLASH)
subwf TBLPTRL, w
movlw high(END_FLASH)
subwfb TBLPTRH, w
movlw upper(END_FLASH)
subwfb TBLPTRU, w
bn WriteEndFlashAddressOkay
WriteEndFlashAddressOkay:
#ifdef USE_SOFTCONFIGWP
#ifdef CONFIG_AS_FLASH
movlw low(END_FLASH - ERASE_FLASH_BLOCKSIZE)
subwf TBLPTRL, w
movlw high(END_FLASH - ERASE_FLASH_BLOCKSIZE)
subwfb TBLPTRH, w
movlw upper(END_FLASH - ERASE_FLASH_BLOCKSIZE)
subwfb TBLPTRU, w
bn WriteConfigAddressOkay
WriteConfigAddressOkay:
#endif ; end CONFIG_AS_FLASH
#endif ; end USE_SOFTCONFIGWP
#ifdef USE_SOFTBOOTWP
movlw low(BOOTLOADER_ADDRESS)
subwf TBLPTRL, w
movlw high(BOOTLOADER_ADDRESS)
subwfb TBLPTRH, w
movlw upper(BOOTLOADER_ADDRESS)
subwfb TBLPTRU, w
bn WriteAddressOkay
movlw low(BOOTLOADER_ADDRESS + BOOTBLOCKSIZE)
subwf TBLPTRL, w
movlw high(BOOTLOADER_ADDRESS + BOOTBLOCKSIZE)
subwfb TBLPTRH, w
movlw upper(BOOTLOADER_ADDRESS + BOOTBLOCKSIZE)
subwfb TBLPTRU, w
bnn WriteAddressOkay
WriteAddressOkay:
#ifdef EEADR
movlw b'10000100' ; Setup FLASH writes
#else
movlw b'00000100' ; Setup FLASH writes for J device (no EEPROM bit)
#endif
movwf EECON1
LoadHoldingRegisters:
movff POSTINC0, TABLAT ; Load the holding registers
pmwtpi ; Same as tblwt *+
decfsz DATA_COUNTL, F
bra WriteFlash ; Not finished writing all blocks, repeat
bra SendAcknowledge ; all done, send ACK packet
; In: <STX>[<0x05><ADDRL><ADDRH><0x00><0x00><BYTESL><BYTESH>]<CRCL><CRCH><ETX>
; Out: <STX>[<DATA>...]<CRCL><CRCH><ETX>
#ifdef EEADR ; some devices do not have EEPROM, so no need for this
code
ReadEeprom:
clrf EECON1
ReadEepromLoop:
bsf EECON1, RD ; Read the data
movf EEDATA, w
#ifdef EEADRH
infsnz EEADR, F ; Adjust EEDATA pointer
incf EEADRH, F
#else
incf EEADR, F ; Adjust EEDATA pointer
#endif
rcall SendEscapeByte
rcall AddCrc
#ifdef EEADRH
decf DATA_COUNTL, f ; decrement counter
movlw 0
subwfb DATA_COUNTH, f
; In:
<STX>[<0x06><ADDRL><ADDRH><0x00><0x00><BYTESL><BYTESH><DATA>...]<CRCL><CRCH><ETX>
; Out: <STX>[<0x06>]<CRCL><CRCH><ETX>
#ifdef EEADR ; some devices do not have EEPROM, so no need for this
code
WriteEeprom:
movlw b'00000100' ; Setup for EEPROM data writes
movwf EECON1
WriteEepromLoop:
movff PREINC0, EEDATA
rcall StartWrite
#ifdef EEADRH
infsnz EEADR, F ; Adjust EEDATA pointer
incf EEADRH, F
#else
incf EEADR, f ; Adjust EEDATA pointer
#endif
#ifdef EEADRH
decf DATA_COUNTL, f ; decrement counter
movlw 0
subwfb DATA_COUNTH, f
WriteConfigLoop:
movf POSTINC0, w
cpfseq TABLAT ; is the proposed value already the same as existing
value?
rcall TableWriteWREG ; write config memory only if necessary (save time and
endurance)
tblrd +* ; increment table pointer to next address and read
existing value
decfsz DATA_COUNTL, F
bra WriteConfigLoop ; If more data available in packet, keep looping
;************************************************
; ***********************************************
; Send an acknowledgement packet back
;
; <STX><COMMAND><CRCL><CRCH><ETX>
; Some devices only have config words as FLASH memory. Some devices don't have
EEPROM.
; For these devices, we can save code by jumping directly to sending back an
; acknowledgement packet if the PC application erroneously requests them.
#ifdef CONFIG_AS_FLASH
WriteConfig:
#else
#ifdef USE_SOFTCONFIGWP
WriteConfig:
#endif
#endif ; end #ifdef CONFIG_AS_FLASH
#ifndef EEADR
ReadEeprom:
WriteEeprom:
#endif
SendAcknowledge:
clrf EECON1 ; inhibit write cycles to FLASH memory
movf COMMAND, w
rcall SendEscapeByte ; Send only the command byte (acknowledge packet)
rcall AddCrc
SendChecksum:
movf CRCL, W
rcall SendEscapeByte
movf CRCH, W
rcall SendEscapeByte
SendETX:
movlw ETX ; Send stop condition
rcall SendHostByte
bra WaitForHostCommand
; *****************************************************************************
; *****************************************************************************
; Write a byte to the serial port while escaping control characters with a DLE
; first.
SendEscapeByte:
movwf TXDATA ; Save the data
movf TXDATA, W
xorlw ETX ; Check for a ETX
bz WrDLE ; No, continue WrNext
movf TXDATA, W
xorlw DLE ; Check for a DLE
bnz WrNext ; No, continue WrNext
WrDLE:
movlw DLE ; Yes, send DLE first
rcall SendHostByte
WrNext:
movf TXDATA, W ; Then send STX
SendHostByte:
clrwdt
btfss UxPIR, UxTXIF ; Write only if TXREG is ready
bra $-2
return
; *****************************************************************************
; *****************************************************************************
ReadHostByte:
btfsc UxRCSTA, OERR ; Reset on overun
reset
WaitForHostByte:
clrwdt
btfss UxPIR, UxRCIF ; Wait for data from RS232
bra WaitForHostByte
return
; *****************************************************************************
; *****************************************************************************
; Unlock and start the write or erase sequence.
TableWriteWREG:
movwf TABLAT
tblwt *
StartWrite:
clrwdt
return
; *****************************************************************************
END