;****************************************************************************** ; Bootloader for Microchip PIC18F258 (c) 2002, Ivar Johnsrud & Petr Kolomaznik ;****************************************************************************** ; Name of file: bootld18F258.asm ; Date: 23.04.2003 * ; Version: 0.5 / rev PF ; Author: Ivar Johnsrud * ; Email: ivar@johnsrud.no * ; Url: www.johnsrud.no/pic * ;****************************************************************************** ; How to use this bootloader: ; - open project bootld18.pjt in MPLAB ; - check and modify of parameters in the user setting section with <<< mark ; - make project with MPLAB and MPASM ; - use any programmer for programming of bootldr18.hex to microcontroller ; - set configuration bits ; - use PIC downloader program for user program download ; - in your user program, do not use program memory space after 0x7D34 ; Notes: ; - tab size for editor is 2 ; - Bootloader and protocol is based on Petr Kolomaznik's bootlader. ; - http://www.ehl.cz/pic/ ; - 18 series flashwrite and delete routines are taken from John B. Peatman ; - http://www.picbook.com ; Disclaimer: ; This code is provided AS IS and with no warrany. Any damage to hardware or ; waste of time is not the authors responsibility. Any questions about this ; program can be posted to ivar@johnsrud.no. Response time for these mails ; can be from minutes to several days. ;****************************************************************************** errorlevel -207, -314, -315 ; no message 207, 314 and 315 ;================== User setting section ====================================== list p=18f258 ; <<< set type of microcontroller ; #define FOSC D'4000000' ; <<< set operating frequence [Hz], max 40Mhz #define FOSC D'20000000' ; <<< set operating frequence [Hz], 20Mhz #define PLL_EN 0 ; <<< use buildt in PLL (freq = 4 x quartz freq) [0|1] #define BAUD D'19200' ; <<< set baud rate [bit/sec] #define BAUD_ERROR D'4' ;<<< set baud rate error [%] #define TIME ; <<< set method of bootloader start PIN/TIME ; PIN : start on low level of trigger pin ; TIME: start on receive IDENT byte in TIMEOUT #define TRIGGER PORTB,3 ; <<< only for PIN - set PORT_X,PIN_NR #define TIMEOUT D'30' ; <<< only for TIME - set time [0.1s], max. 25 sec #define WATCHDOGTIMER 0 ; <<< Watchdog timer default OFF/ON [0|1] ;=================== Configuration ============================================ IF WATCHDOGTIMER == 0 #define MY_WDT _WDT_OFF_2H ELSE #define MY_WDT _WDT_ON_2H ENDIF IF PLL_EN == 0 #define MY_OSC _HS_OSC_1H ELSE #define MY_OSC _HSPLL_OSC_1H ENDIF #include list P=PIC18F258, F=INHX32, C=160, N=0, ST=OFF, MM=OFF, R=DEC, X=ON ; Add config definitions missing in p18f258.inc. Not complete ; for code protection. _DEBUG_OFF_4L EQU H'FF' _DEBUG_ON_4L EQU H'7F' _LVP_OFF_4L EQU H'FB' _LVP_ON_4L EQU H'FF' _CONFIG5L EQU H'300008' _CONFIG5H EQU H'300009' _CONFIG6L EQU H'30000A' _CONFIG6H EQU H'30000B' _CONFIG7L EQU H'30000C' _CONFIG7H EQU H'30000D' __CONFIG _CONFIG1H, MY_OSC & _OSCS_OFF_1H ; My osc.; osc. switch dis. __CONFIG _CONFIG2L, _BOR_OFF_2L & _PWRT_ON_2L ; BOR disabled; PWRT enabled __CONFIG _CONFIG2H, MY_WDT ; Watchdog timer disabled __CONFIG _CONFIG4L, _DEBUG_OFF_4L & _LVP_OFF_4L ; Background debugger disabled ; Low-voltage ICSP disabled __CONFIG _CONFIG5L, 0xFF ;No code protection. __CONFIG _CONFIG5H, 0xFF ; __CONFIG _CONFIG6L, 0xFF ; __CONFIG _CONFIG6H, 0XFF ; __CONFIG _CONFIG7L, 0xFF ; __CONFIG _CONFIG7H, 0xFF ; #define ProgHI 0x7FFF ;=============== End of user setting section ================================== ;================== Check User Constants ====================================== IFNDEF FOSC ERROR "FOSC must be defined" ENDIF IFNDEF BAUD ERROR "BAUD must be defined" ENDIF IF FOSC > D'40000000' ERROR "max. frequency = 40 MHz" ENDIF IFNDEF PIN IFNDEF TIME ERROR "wrong start method of bootloader, must be PIN or TIME" ENDIF ENDIF IF TIMEOUT > D'254' ERROR "max. timeout = 25.4 sec" ENDIF ;========================== Constants ========================================= IF ((FOSC/(D'16' * BAUD))-1) < D'256' #define DIVIDER (FOSC/(D'16' * BAUD))-1 #define HIGH_SPEED 1 ELSE #define DIVIDER (FOSC/(D'64' * BAUD))-1 #define HIGH_SPEED 0 ENDIF BAUD_REAL EQU FOSC/((D'64'-(HIGH_SPEED*D'48'))*(DIVIDER+1)) IF BAUD_REAL > BAUD IF (((BAUD_REAL - BAUD)*D'100')/BAUD) > BAUD_ERROR ERROR "wrong baud rate" ENDIF ELSE IF (((BAUD - BAUD_REAL)*D'100')/BAUD) > BAUD_ERROR ERROR "wrong baud rate" ENDIF ENDIF IF FOSC > D'10240000' #define T1PS 8 #define T1SU 0x31 ELSE IF FOSC > D'5120000' #define T1PS 4 #define T1SU 0x21 ELSE IF FOSC > D'2560000' #define T1PS 2 #define T1SU 0x11 ELSE #define T1PS 1 #define T1SU 0x01 ENDIF ENDIF ENDIF IF (FOSC > D'20480000') TIMER EQU 0 ; 0.1s int not correct anymore. int = 0.065s@40Mhz ELSE TIMER EQU (D'65538'-(FOSC/(D'10'*4*T1PS))); reload value for TIMER1 (0.1s int) ENDIF #define LoaderSize 0x2CC ; size of bootloader (must be n*40h+Ch) #define LoaderMain ProgHI-LoaderSize+0x0D ; Main address of bootloader. Must be ; at start of a 64 byte block. #define LoaderStart ProgHI-LoaderSize+1 ; start address of bootloader UserMemory EQU LoaderMain ; Why is this line necessarry? Num_64_blocks EQU UserMemory/64; #define SEG 0xE2 ; Added for inhx32 comp #define WRITE 0xE3 ; communication protocol #define WR_OK 0xE4 #define WR_BAD 0xE5 #define LIN 0xE6 ; Added for inhx32 comp #define DATA_OK 0xE7 #define DATA_BAD 0xE8 #define IDENT 0xEA #define IDACK 0xEB #define DONE_ 0xED ;=============== Variables ==================================================== ADDRH EQU 0x00 ADDRL EQU 0x01 countt EQU 0x02 TEMP1 EQU 0x0A COUNT1 EQU 0x0B buff EQU 0x20 ; must be at least 22 bytes. startb EQU 0x40 ; must be at least 8 bytes. ; RAM address 0x70 reserved for MPLAB-ICD chk1 EQU 0x71 chk2 EQU 0x72 time EQU 0x73 count EQU 0x74 inst EQU 0x75 offset EQU 0x76 numwords EQU 0x77 t1 EQU 0x78 ;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF macro literal,dest ;Move literal to register movlw literal movwf dest endm ;=============== Program start ================================================ ; The reset vector gets erased before each download. These instructions are ; reprogrammed after each erase. ORG 0x0000 ; reset vector of microcontroller goto Main nop ;------------------------------------------------------------------------------ ; TrapError and UserStart is at the end of the last free 64 bytes userblock. ; This is because flash erase is done in 64 byte blocks. The goto TrapError ; is reprogrammed each time, and the user reset vector is moved to UserStart. ORG LoaderStart TrapError goto TrapError ; trap for unintended running into Bootloader UserStart ; the following 4 instruction words get overwritten by user program nop nop goto UserStart ; loop for first start without a user program ;------------------------------------------------------------------------------ ORG LoaderMain Main btfss RCON,NOT_TO ; run user program after WatchDog-TimeOut goto UserStart start movlw 0x90 ; SPEN = 1, CREN = 1 movwf RCSTA IF HIGH_SPEED == 1 ; USART SYNC=0; SPEN=1; CREN=1; SREN=0; bsf TXSTA,BRGH ; TX9=0; RX9=0; TXEN=1; ELSE bcf TXSTA,BRGH ENDIF bsf TXSTA,TXEN movlw DIVIDER ; baud rate generator movwf SPBRG IFDEF PIN ; for PIN: TRIS already 1 btfss TRIGGER goto receive ; go to bootloader goto user_restore ; run user program ELSE movlw TIMEOUT+1 ; for TIME: set timeout movwf time movlw T1SU movwf T1CON ; TIMER1 on, internal clock, prescale T1PS bsf PIR1,TMR1IF call getbyte ; wait for IDENT xorlw IDENT btfss STATUS,Z goto user_restore clrf time ; no more wait for IDENT goto inst_ident ; bootloader identified, send of IDACK ENDIF ;---------------------------------------------------------------------------- receive ; programming call getbyte ; get byte from USART movwf inst xorlw WRITE btfsc STATUS,Z goto inst_write ; write instruction movf inst,w xorlw LIN btfsc STATUS, Z goto inst_write ; Linear address instruction movf inst,w xorlw IDENT btfsc STATUS,Z goto inst_ident ; identification instruction movf inst,w xorlw DONE_ btfss STATUS,Z ; instruction ? goto receive ;--------------------------------------------------------------------------- inst_done ; very end of programming ;--------------------------------------------------------------------------- movlw WR_OK call putbyte ; send of byte movlw TIMEOUT+1 movwf time call getbyte ; has built in timeout - waits until done ;---------------------------------------------------------------------------- user_restore clrf T1CON ; shuts off TIMER1 clrf RCSTA clrf TXSTA ; restores USART to reset condition clrf PIR1 bsf PORTA, 4 ; light alive led. goto UserStart ; run user program ;------------------------------------------------------------------------------ inst_ident call EraseMemory ; Must erase flash memory before writing. call RestoreLoader ; Restores reset vector and TrapError movlw IDACK ; send IDACK goto send_byte ;------------------------------------------------------------------------------ inst_write call getbyte ; Get high byte of program start address movwf ADDRH call getbyte ; Get low byte of program start address movwf ADDRL andlw B'00000111' ; Mask out 3 lsb. movwf offset ; Save offset in 8 byte block. lfsr 0, buff ; FSR0 pointer = buff fill_head_ff ; Status not affected since mask applied, check if result was zero. bz num_bytes ; Receive data if zero (aligned). MOVLF 0xFF, POSTINC0 ; Fill start of block with FF (data will not change when programming FF). decf ADDRL, F ; Do this until 3 lsb is zero. movf ADDRL, W andlw B'00000111' ; Mask out 3 lsb. bra fill_head_ff ; Continue until done. ; ADDRL will now be at start of an 8 byte block, and the receive buffer will be ; initialized correctly. num_bytes call getbyte movwf numwords ; number of bytes -> numwords rrncf numwords ; must div/2 (count should be even => lsb = 0) movwf count ; number of bytes -> count call getbyte ; checksum -> chk2 movwf chk2 clrf chk1 ; chk1 = 0 receive_data call getbyte ; must swap order on bytes movwf t1 ; => low byte = low address addwf chk1, F call getbyte movwf POSTINC0 addwf chk1, F movff t1, POSTINC0 decf numwords, F ; Received one word. bnz receive_data ; Receive until done checksum movf chk1,w xorwf chk2,w ; if (chk1 != chk2) movlw DATA_BAD btfss STATUS,Z bra send_byte ; checksum WRONG checksum_ok movlw DATA_OK ; checksum OK call putbyte movf offset, W addwf count, F fill_tail_ff movf count, W andlw B'00000111' ; Mask out 3 lsb. bz write_byte ; If aligned to 8 bytes, done. MOVLF 0xFF, POSTINC0 ; Fill end of block with FF (data will not change when programming FF). incf count, F bra fill_tail_ff ; Continue write_byte ; Divide count by 3 to get number of blocks to write. rrncf count, F ; 3 lsb is zero, so divide by eight should be same as rrncf count, F ; 3 right rotate. rrncf count, F ; ADDRH:ADDRL will now point at start address of first block to write, and ; buff will contain the data to be programmed. The count is number of blocks ; to write. call write_eeprom ; write to eeprom iorlw 0 movlw WR_OK ; writing OK btfsc STATUS,Z movlw WR_BAD ; writing WRONG ;*********************** subroutines ********************************* send_byte call putbyte ; send of byte goto receive ; go to receive from UART putbyte clrwdt btfss PIR1,TXIF ; while(!TXIF) goto putbyte movwf TXREG ; TXREG = byte return getbyte clrwdt IFNDEF PIN ; for TIME movf time,w btfsc STATUS,Z ; check for time==0 goto getbyte3 btfss PIR1,TMR1IF ; check for TIMER1 overflow goto getbyte3 ; no overflow bcf T1CON,TMR1ON ; timeout 0.1 sec decfsz time,f ; time-- goto getbyte2 retlw 0 ; if time==0 then return getbyte2 bcf PIR1,TMR1IF movlw high TIMER movwf TMR1H ; preset TIMER1 for 0.1s timeout bsf T1CON,TMR1ON ENDIF getbyte3 btfss PIR1,RCIF ; while(!RCIF) goto getbyte movf RCREG,w ; RCREG return ;******************** write eeprom subroutine ********************************* write_eeprom movf inst,W ; get instruction type for testing xorlw LIN btfsc STATUS,Z ; Z=1 if Linear Record Type bra LinearRec ; Handle Linear Record Case movf inst,W xorlw WRITE ; Z=1 if write btfss STATUS,Z retlw 0 ; Z=0 if invalid Record Type ; Should check if writing results in ; corrupt bootloader before writing. movf ADDRH, W bnz adr_ok ; Check if adr < 0x0004 movlw 0x04 subwf ADDRL, W bc adr_ok MOVLF HIGH UserStart, ADDRH ; Move to userstart. MOVLF LOW UserStart, ADDRL ; Must detect the goto instruction and move it to userstart. ; While this is done, the code is copied to startb and start ; of buff is zeroed (0xFF'ed). lfsr 1, buff lfsr 0, startb MOVLF 0xFF, numwords ; reusing a variable copy_prog_data movff INDF1, POSTINC0 ; Copy program data movf INDF1, W movff numwords, POSTINC1 ; and insert a nop instead. xorlw 0xEF ; Check if goto instruction. bnz copy_prog_data ; When start of goto is found, 2 next bytes must be copied, before done. movff INDF1, POSTINC0 ; Copy program data MOVLF 0xFF, POSTINC1 ; and insert a nop instead. movff INDF1, POSTINC0 ; Copy program data movwf POSTINC0 movwf POSTINC0 movwf POSTINC0 movwf POSTINC0 movff count, countt ; Save temp count. MOVLF 0x01, count lfsr 0, startb rcall ProgramFlash ; Program moved reset vector. clrf ADDRL clrf ADDRH movff countt, count adr_ok lfsr 0, buff rcall ProgramFlash ; Program the line received retlw 1 LinearRec: ; If instruction is Linear Record movff buff+1,TBLPTRU ; Move Upper Address Byte into W ; No need to check for overwrites, since the retlw 1 ; downloader software does that. ;*************** Program Flash subroutine ************************* ; Routine programs 'count' blocks into program memory, starting at ADDRH:ADDRL, ; reading from INDF0. ProgramFlash movff ADDRH,TBLPTRH ; Address in program memory to write to. movff ADDRL, TBLPTRL ; Now Ready to Write. Write call FlashWrite ; Write 8 bytes to flash. decf count, F bnz Write ; Continue until all blocks written. return ;*************** erase memory subroutine *************************** ; Routine erases memory in 64 byte segments. Erases Num_64_blocks memory blocks ; starting at 0x0000. EraseMemory clrf TBLPTRU ; Start erasing at address 0 clrf TBLPTRH clrf TBLPTRL lfsr 2, Num_64_blocks ; load FSR2 with Num_64_blocks (502 for 0x7D80) Repeat_erase clrwdt rcall FlashErase64 ; Erase Current 64 byte segment movlw 64 ; Increment TBLPTR to next 64 addwf TBLPTRL,F ; byte segment btfsc STATUS,C ; If rollover then increment incf TBLPTRH,F ; high byte movf POSTDEC2,F movf FSR2L,F bnz Repeat_erase movf FSR2H,F bnz Repeat_erase ; Repeat until FSR2 = 0x000 return ;************** Restore loader subroutine ************************ ; RestoreLoader - routine inserts a goto Main from address 0x0000. ; It also inserts a goto TrapError at TrapError. RestoreLoader lfsr 0, buff ; Construct instructions in buff movlw UPPER Main ; Upper byte of address to main movwf t1+3 bcf STATUS, C ; Clear carry bit rrcf t1+3, F ; Rotate through carry movlw B'11110000' ; Second word in goto must start with F. iorwf t1+3, F movlw HIGH Main ; High byte of address to main. movwf t1+2 rrcf t1+2, F movlw LOW Main ; Low byte of address to main movwf t1 rrcf t1, F MOVLF 0xEF, t1+1 ; High byte in first word is EF lfsr 1, t1 MOVLF 0x04, count rest_rep movff POSTINC1, POSTINC0 decf count, F bnz rest_rep movlw 0xFF ; two last nop movwf POSTINC0 ; movwf POSTINC0 ; movwf POSTINC0 ; buff should now contain 8 bytes : movwf POSTINC0 ; goto Main, nop, nop clrf TBLPTRL clrf TBLPTRH clrf TBLPTRU ; Start to write at 0x0000 lfsr 0, buff call FlashWrite ; Write 8 bytes to reset vector. movlw UPPER TrapError ; Upper byte of address to TrapError movwf t1+3 bcf STATUS, C ; Clear carry bit rrcf t1+3, F ; Rotate through carry movlw B'11110000' ; Second word in goto must start with F. iorwf t1+3, F movlw HIGH TrapError ; High byte of address to TrapError. movwf t1+2 rrcf t1+2, F movlw LOW TrapError ; Low byte of address to TrapError. movwf t1 rrcf t1, F MOVLF 0xEF, t1+1 ; High byte in first word is EF lfsr 0, buff movlw 0xFF movwf POSTINC0 movwf POSTINC0 movwf POSTINC0 movwf POSTINC0 lfsr 1, t1 MOVLF 0x04, count rest_rep1 movff POSTINC1, POSTINC0 decf count, F bnz rest_rep1 ; buff should now contain 8 bytes : ; nop, nop, goto TrapError MOVLF UPPER TrapError, TBLPTRU MOVLF HIGH TrapError, TBLPTRH movlw LOW TrapError andlw B'11111000' ; Mask out 3msb for 8 byte alignment. movwf TBLPTRL lfsr 0, buff call FlashWrite ; Write 8 bytes. return ;************ Flash Write subroutine **************** ; Writes 8 bytes from RAM address pointed to by FSR0 to Program Memory address ; contained in TBLPTR FlashWrite MOVLF 8,TEMP1 ; Read 8 bytes to write WriteByte movff POSTINC0,TABLAT ; Get byte and present data to table latch TBLWT*+ ; Write data decf TEMP1, F ; Loop until buffers are full bnz WriteByte WriteMemory TBLRD*- ; Must point to correct 8 byte block bsf EECON1,EEPGD ; Point to FLASH program memory bsf EECON1,WREN ; Enable write to memory call WriteInitiateSequence bcf EECON1,WREN ; Disable write to memory TBLRD*+ ; Count to next address when done. return ;************* Flash Erase subroutine ******************** ; Erases a 64-byte block pointed to by TBLPTR FlashErase64 bsf EECON1,EEPGD ;point to FLASH program memory bsf EECON1,WREN ;enable write to memory bsf EECON1,FREE ;enable row erase operation call WriteInitiateSequence return WriteInitiateSequence MOVLF 0x55,EECON2 ;write Hex 55 to EECON2 Reg MOVLF 0xAA,EECON2 ;write Hex AA to EECON2 Reg bsf EECON1,WR ;start erase or write operation nop return END