;---------------------------------------------------------------------------------------- ; ; Program: Bootloader_Mega8.asm ; ; Writer: Herbert Dingfelder, DL5NEG ; for contact information visit WWW.DL5NEG.DE ; ; Based on: AVR-Freaks Design Note #32 ; ; Function: Bootloader for ATmega8, Works via the UART with the normal ; AVRProg PC-Software ; ; Hardware: ATmega8 with a 7.3728MHz crystal ; ; History: ; ; 13.07.2004 - Source code adapted for Mega8, assembles without errors ; ; 15.07.2004 - Used crystal is not 7.3 but 3.686 MHz -> UART setting adapted ; - LED on LEDport/LED is switched on within the boot section, ; tested in Hardware and works -> processor starts correctly ; within the boot section ; 18.07.2004 - Problem how to download the bootloader is solved: Simply use ; the AVRProg in the "mega8" mode, not in "mega8boot" mode. ; Since the Intel-Hex Format contains the address for the code ; in each line, the programmers recognizes that the code has to ; go to the bootrom part of the flash (provide that the ; ".org MY_BOOTSTART" is put at the beginning of the source code) ; - UART communication between bootloader and host PC works ; (echo mode and character recognition tested and works) ; - Crystal changed to 7.3728MHz in an attempt to fight "flushing" ; problems in AVRProg (without success), UART setting adapted ; - AVRProg is able to read the flash correctly (tested, works) ; - Reading and writing to EEPROM on host commands 'D' and 'd' works. ; - Flash erase did not work in the NRWR section (the last 1024 ; words of the flash) The original version from DesignNote #32 ; was for ATmega163 which does not have RWR/NRWR sections. ; Problem solved by implementing the wait_for_spm function that ; makes sure that a new SPM does only start when the previous one ; is completed. (tested, works now) ; - function to reenable the RWW section implemented -> ; loading data into the application section tested and works fine ; (Still some problems with sync on the interface between AVRProg ; and the boot loader, messagebox "flushing" appears, fuse bits are ; read differently many times and software download needs to be ; started twice sometimes. As it is known that AVRProg is very sensible ; command/answer sync, this does not point towards real bootloader problems. ; It has to be tracked on the interface.) ; -> Good enough to work with temporarily, Version 1.0 released. ; - Startup-function implemented for 10 second wait after power-on. If ; no ASCII(27) is received via UART within that time, the application starts. ; If the application section is empty (all cells are $FF) e.g. directly ; after uploading the bootloader itself, then software will wait forever ; for a ESC via the UART to start the bootloader software ; (while waiting for the ESC the LED blinks) ; - Function pause implemented for delays in steps of 10ms (tested works) ; - Command 'E' implemented for leaving the bootloader mode and start the ; application ; - With that entry end exit function the bootloader is fully usable, starting ; the downloaded application is tested and works perfectly ; -> Version 1.1 released ; - Still known problem: the above mentioned sync problem in combination ; with AVRProg have to be identified and solved. ; - Only 2 Words are left in the bootloader memory. If Bugfixing needs more, ; the erease counter within the EEPROM can be skipped ; 24.07.2004 - Debug Session with Andy, DG9NDZ to find why AVRProg 1.37 still has some ; problems with the bootloader, especially in the "Advanced" Window. ; * Command 'v' for Hardware Version was not implemented -> fixed ; * Command ':' for tranparent command setting was not implemented ; -> is now implemented in simple version, always returning \0 ; By monitoring the PC/AVR interface it was found that AVRProg1.37 does ; not use the defined commands r/F/N for reading the lock and fuse bits, ; but uses the universal command ':' to send the programming instructions ; directly. Additionally the calibration byte is read by the ':' command. ; * Command 'b' is send by AVRProg1.37 but is not implemented. It was found that ; it is fatal for the programming mode to implement 'b' to return 'Y', 'Y'. ; To simply leave it out (returns ? for unknow command) works perfectly. ; 25.07.2004 - To implement all improvements found on 24.07.2004 without skipping lots of ; of other code made it necessary to increase the boot loader size to 512words ; (=1kByte). Code adapted. ; - Command ':' enlarged to map r/F/N functionality ; - Tested, current status: Works well in AVRProg both for flash reading and writing ; and also in the Advance window the lock and fuse bits are display correctly. ; Only week point remaining to press "write" in advance window, that causes a ; loose of sync between host and bootloader. No harm is done to the target device. ; -> Version 1.2 released. ; 26.07.2004 - Monitoring of interface from host reveals the root cause of the sync problem when ; writing the the lock and fuse bits. AVRProg1.37 uses the "new universal command" ; which is "." + 4 data bytes. This command was not implemented. As changing the ; fuse bits is dangerous when the device is remote (one could lock himself out ; forever), the command is now implemented simply to ignore the inputs but correctly ; serve the interface to the host to avoid sync problems. Tested and works perfectly. ; -> Version 1.3 release ; ; ; ; Before or after programming a new ATmega8 with this Boot Loader you must program Fuse ; bits as follows: ; ; • The ATmega8 Fuse High bits must be programmed to 0x04 in order to configure ; the Boot size to 256 words and to move the Reset Vector to word address 0x1f00. ; ; I.e. set the check box for BOOTRST in AVR-Prog-Advanced and set the ; selector to 256words, then press "write". It is important that this selected ; size of the bootrom fits to the .org command within this source code, otherwise ; the processor will not correctly start at the first line of this code after reset. ; (Settings can be checked now or later with "read"). ; ; Optional: ; ; • The Lock bits can be programmed to 0xEF to protect the Boot Flash Section from ; unintentional changes and to allow accessing the Application Flash Section. ; ; I.e. the setting for BLB0 and BLB1 in AVR-Prog-Advance: ; ; BLB0 (sets the protection for the application section) must be set to ; Mode 1 (no protection) to allow full access of LPM and SPM commands within ; the application section. ; ; BLB1 (sets the protection for the boot loader section) can be set to Mode 2 ; (SPM cannot write to boot section) or can be left to Mode 1 (no protection). ; ; • The Fuse bits must be programmed before programming the Lock bits. ; ; ; AVRProg Commands as defined by Atmel ; ; Host Writes Host Reads ; ID Data Data ; Enter Programming Mode 'P' 13d (implemented to ignore) ; Auto Increment Address 'a' dd (fully implemented) ; Set Address 'A' ah al 13d (fully implemented) ; Write Program Memory, Low Byte 'c' dd 13d (fully implemented) ; Write Program Memory, High Byte 'C' dd 13d (fully implemented) ; Issue Page Write 'm' 13d (fully implemented) ; Read Lock Bits 'r' dd (fully implemented) ; Read Program Memory 'R' 2*dd (fully implemented) ; Read EEPROM Memory 'd' dd (fully implemented) ; Write EEPROM Memory 'D' dd 13d (fully implemented) ; Chip Erase 'e' 13d (implemented, erases appl. area only) ; Write Lock Bits 'l' dd 13d (implemented to ignore) ; Read Fuse Bits 'F' dd (fully implemented) ; Read High Fuse Bits 'N' dd (fully implemented) ; Read Extended Fuse Bits 'Q' dd (not implemented) ; Leave Programming Mode 'L' 13d (implemented to ignore) ; Select Device Type 'T' dd 13d (implemented to ignore) ; Read Signature Bytes 's' 3*dd (fully implemented) ; Return Supported Device Codes 't' n*dd 00d (implemented, returns only Mega8) ; Return Software Identifier 'S' s[7] (fully implemented) ; Return Software Version 'V' dd dd (fully implemented) ; Return Hardware Version 'v' (fully implemented) ; Return Programmer Type 'p' dd (fully implemented) ; Set LED 'x' dd 13d (implemented to ignore) ; Clear LED 'y' dd 13d (implemented to ignore) ; Exit Bootloader 'E' {13d} (implemented 'E' is not confirmed) ; Check Block Support 'b' 'Y' 2*dd (implemented to return no) ; Start Block Flash Load 'B' 2*dd 'F' 13d (not implemented) ; n*dd ; Start Block EEPROM Load 'B' 2*dd 'E' 13d (not implemented) ; n*dd ; Start Block Flash Read 'g' 2*dd 'F' n*dd (not implemented) ; Start Block EEPROM Read 'g' 2*dd 'E' n*dd (not implemented) ; Universal Command (3 data bytes) ':' 3*dd dd 13d (implemented to ignore) ; New Universal Command (4 data bytes)'.' 4*dd dd 13d (implemented to ignore) ; ;-------------------------definition and includes --------------------------------------- .INCLUDE "m8def.inc" ; Include Register/Bit Definitions for the mega8 .def tmp_r = r18 ; Use Reg. R18 for temporary stuff .def pa_10ms = r19 ; used for selecting the pause time in steps of 10ms .def uni_cmd1 = r20 ; Universal command ':' send three bytes. These .def uni_cmd2 = r21 ; are stored for futher usage in r20, r21, r28 .def uni_cmd3 = r10 .equ VER_H = '1' ; bootloader software version higher number .equ VER_L = '3' ; bootloader software version lower number .equ VERH_H = '1' ; bootloader hardware version higher number .equ VERH_L = '0' ; bootloader hardware version lower number .equ DT = 0x76 ; Device Type = 0x76 (ATmega8) .equ SB1 = 0x07 ; Signature byte 1 .equ SB2 = 0x93 ; Signature byte 2 .equ SB3 = 0x1e ; Signature byte 3 .equ ESC = 27 ; Ascii 27 = Escape ;LR***************************************************** .equ fCK = 7372800 ;X-tal frequency * .equ BaudR=115200 ;Baud rate * .equ Tr_Sp=0 ;Transmission speed (U2X) * .equ UBR=-1+(5+10*fCK/(16*BaudR-8*BaudR*Tr_Sp))/10 ; * .equ presc=1024 ; * .equ fTmr=100 ;freq. of Timer 0 (1/10ms=100) * .equ TmrLoad=256-(fCK/(presc*fTmr)) ; * .equ CR = 13 ; * ; * ;Pin for LED and Boot escape definition: * .equ LEDport =PORTc ; * .equ LEDdir =DDRc ; * .equ LED =5 ; * .equ LED2 =4 ; * .equ LED3 =2 ; * .equ LED4 =1 ; * ; * .equ TestPort =PINC ; * .equ TestBit =0 ; * .equ ActiveStatLED=0 ;0=low, 1=high active stat .if ActiveStatLED .macro set_led sbi LEDport,LED .endm .macro clr_led cbi LEDport,LED .endm .else .macro set_led cbi LEDport,LED .endm .macro clr_led sbi LEDport,LED .endm .endif ;LR***************************************************** .equ MY_BOOTSTART = THIRDBOOTSTART ; here we define where the bootloader starts in the flash ;----------------------------- here we go ----------------------------------------------- .org MY_BOOTSTART ; bootstart address according to .equ above ; ---------------------------------- Boot Escape ------------------------------------------ ;LR*********************************** in tmp_r,TestPort ; * sbrc tmp_r,TestBit ; * rjmp FLASHEND+1 ; * ;LR*********************************** sbi LEDdir, LED2 ; sbi LEDport,LED2 ; sbi LEDdir, LED3 ; cbi LEDport,LED3 ; sbi LEDdir, LED4 ; sbi LEDport,LED4 ; ; ----------------------------------- init stackpointer --------------------------------- ldi R24, low(RAMEND) ; SP = RAMEND ldi R25, high(RAMEND) out SPL, R24 out SPH, R25 ldi tmp_r, 0b00000101 ; set prescaler to 1024 out TCCR0, tmp_r ; ---------------------------------- init UART ------------------------------------------ ;LR*********************************** .if Tr_Sp ; * sbi UCSRA,U2x ; * .else ; * cbi UCSRA,U2x ; * .endif ; * ;LR*********************************** ldi tmp_r, LOW(UBR) out UBRRL, tmp_r .if HIGH(UBR) ldi tmp_r, HIGH(UBR) out UBRRH, tmp_r .endif ldi tmp_r,(1< 58 00 is read lock bits, ldi ZL,1 ; Z pointer = 0001 (-> readFuseAndLock will read lock) rcall readFuseAndLock ; go get the requested bits cmd2_not_0x00_1: cpi uni_cmd2, 0x08 ; check if 2nd paramter is 0x08 brne cmd2_not_0x08 ; -> 58 08 is read fuse high bits, ldi ZL,3 ; Z pointer = 0003 (-> readFuseAndLock will read high fuse) rcall readFuseAndLock ; go get the requested bits cmd2_not_0x08: cmd1_not_0x58: cpi uni_cmd1, 0x50 ; check if 1st paramter is 0x50 brne cmd1_not_0x50 cpi uni_cmd2, 0x00 ; check if 2nd paramter is 0x00 brne cmd2_not_0x00_2 ; -> 50 00 is read fuse bits, clr ZL ; Z-pointer = 0000 (-> readFuseAndLock will read fuse) rcall readFuseAndLock ; go get the requested bits cmd2_not_0x00_2: cmd1_not_0x50: rcall uartSend ; uartSend(bits) send the read lock/fuse bits rjmp L68 ; uartSend('\r') and go up for next command ;--------------------- Command 'F' is "read fuse bits" ---------------------------------- L44: cpi R16,'F' ; else if(R16=='F') Read fuse bits brne L46 clr ZL ; Z-pointer = 0000 (-> readFuseAndLock will read fuse) rjmp L50 ; rcall readFuseAndLock ;--------------------- Command 'r' is "read lock bits" ---------------------------------- L46: cpi R16,'r' ; else if(R16=='r') Read lock bits brne L48 ldi ZL,1 ; Z pointer = 0001 (-> readFuseAndLock will read lock) rjmp L50 ; rcall readFuseAndLock ;--------------------- Command 'N' is "read fuse high bits" ----------------------------- L48: cpi R16,'N' ; else if(R16=='N') Read high fuse bits brne L52 ldi ZL,3 ; Z-pointer = 0003 (-> readFuseAndLock will read high fuse) ;-------- for all previous commands F,r,N hear we call the readFuseAndLock -------------- ; function that will read the bits that are indicated by the Z-register L50: rcall readFuseAndLock rjmp L70 ; uartSend(R16) ;--------------------- Command 't' is Return supported devices code --------------------- ; obviously we return only the device code of the mega8 here L52: cpi R16,'t' ; else if(R16=='t') Return supported devices code brne L54 ldi R16,DT ; Device Type rcall uartSend ; uartSend(DT) send Device Type of Mega8 clr R16 ; command set of AVRProg requires the termination of ; the t-command with a \0 not with a CR as other commands rjmp L70 ; uartSend(0) and go up for next command ; ---------------- ignored commands that are not sensible for boot loader --------------- ; The following 4 commands only make sense for a general programmer, not for a bootloader. ; As theres commands come with an additional byte as parameter, with have to fetch that ; byte from the UART to avoid messing up the UART reading. L54: cpi R16,'l' ; 'l' = Write Boot Loader lockbits breq L56 cpi R16,'x' ; 'x' = Set LED breq L56 cpi R16,'y' ; 'y' = Clear LED breq L56 cpi R16,'T' ; 'T' = Select device type brne L60 L56: rcall uartGet ; R16 = uartGet() rjmp L68 ; uartSend('\r') and go up for next command ;--------------------- Command 'S' is Return software identifier ------------------------ L60: cpi R16,'S' ; else if(R16=='S') Return software identifier brne L62 ldi ZL,low(2*Soft_Id) ; load address of String into Z-Register ldi ZH,high(2*Soft_Id) L61: lpm R16,Z+ ; read one character of the string and increment string pointer tst R16 breq L72 ; exit the character output loop charcter was '\0' rcall uartSend ; send the read character via the UART rjmp L61 ; go to start of loop for next character ;--------------------- Command 'V' is Return software Version --------------------------- L62: cpi R16,'V' ; else if (R16=='V') Return Software Version brne L63 ldi R16, VER_H ; send bootloader software version higher number rcall uartSend ldi R16, VER_L ; send bootloader software version lower number rjmp L70 ; uartSend and go up for next command ;--------------------- Command 'v' is Return hardware Version --------------------------- L63: cpi R16,'v' ; else if (R16=='v') Return hardware Version brne L64 ldi R16, VERH_H ; send bootloader software version higher number rcall uartSend ldi R16, VERH_L ; send bootloader software version lower number rjmp L70 ; uartSend and go up for next command ;--------------------- Command 's' is Return Signature Byte ----------------------------- L64: cpi R16,'s' ; else if (R16=='s') Return Signature Byte brne L65 ldi R16,SB1 ; uartSend(SB1) Signature Byte 1 rcall uartSend ldi R16,SB2 ; uartSend(SB2) Signature Byte 2 rcall uartSend ldi R16,SB3 ; uartSend(SB3) Signature Byte 3 rjmp L70 ; uartSend and go up for next command ;--------------------- Command '.' is new universal command ----------------------------- ; this command will be ignored, only the inteface will be handled correctly L65: cpi R16,'.' ; else if (R16=='.') New Universal Command brne L66 ;.............. read in the 4 parameters ............................... rcall uartGet ; R16 = uartGet() rcall uartGet ; R16 = uartGet() rcall uartGet ; R16 = uartGet() rcall uartGet ; R16 = uartGet() ; ...................................................................... clr R16 ; uartSend(/0) rcall uartSend rjmp L68 ; uartSend('\r') and go up for next command ;++++++++++++++ handling the different command till here ++++++++++++++++++++++++++++++++ ; only general completion for all commands below ;---------------------- failed commands can end up here --------------------------------- ; an '?' is send via the UART to indicate fail host, then main loop starts again L66: ldi R16,'?' ; else uartSend('?') rjmp L70 ; uartSend(R16) ;--------- successfully completed commands without return value can end up here --------- ; an CR is sent via the UART to confirm execution to host, then main loop starts again L68: ldi R16,13 ; uartSend('\r') ;------------ successfully completed commands with return value can end up here --------- ; the return value is sent via the UART to confirm execution to host ; then main loop starts again L70: rcall uartSend ; uartSend(R16) L72: rjmp L10 ; jump up to beginning of main loop ;-------------------------- end of main loop -------------------------------------------- ;================= end of main program, only subroutines from here on =================== ;------------------ reads fuse or lock bits (depending on Z-Reg.) ------------- readFuseAndLock: ; An LPM instruction within three cycles after BLBSET and SPMEN are set in the SPMCR ; Register, will read either the Lock bits or the Fuse bits ; (depending on Z0 in the Z pointer) into the destination register. rcall wait_for_spm ; make sure SPM is ready to accept a new task clr ZH ; Z pointer high byte = 0 ldi R24,(1<