Operation of Luna bootloader

Summary of the contribution to the explanation of the boot loader functionality from the Luna-Forum.

A so-called bootloader is a small program that is executed beforce starting the user program, dependent on the fusebits of the controller. A boot loader is also stored in the same flash memory of the controller as the user program, but with the difference that it is placed just before the end of the flash memory on certain boundary addresses. The address limit is the einspringpunkt, the controller starts after a hardware reset if the Fusebit BOOTRST has been activated. Which of the usually four different border areas is started, determine the fusebits BOOTSZ0 and BOOTSZ1. The special thing about it is that a program from the boot loader section can writing flash pages (except for the area in which the boot loader itself is located).

Depending on the controller put the two fusebits BOOTSZ0 and BOOTSZ1 determine what the size should be the bootloader area. The size of the bootloader program here determined what size to choose. The available sizes vary between the different controllers, thus it is necessary to look up the values in the data sheet.

Is a boot loader on the controller uploaded, it looks in the flash memory of the controller then, for example like this:

S----------------------------------------|-----|xxxxx|xxx|x--e
                                         :     :     :   :
                                         :     :     :   +FIRSTBOOTSTART
                                         :     :     +SeCONDBOOTSTART
                                         :     +-THIRDBOOTSTART
                                         +FOURTHBOOTSTART
S = start of flash memory
e = end of flash memory
| = bootloader borders
x = bootloader programm

If the boot loader is flashed normally, the controller starts from the set address, in this case, as in the screenshot above with activated Fusebit BOOTSZ1 THIRD BOOT START.

The Luna boot loader checks 10x with very short breaks if a command has arrived that puts him in the flash mode, otherwise the boot loader jumps via the command „ReSeT“ to address 0x0000 and thus to a stored there, the user program. If there is none to be found, the program counter moves through the entire flash to the bootloader where it is running again. This is because is written in flashing in the unused areas of the value 0xff, which is the machine instruction „nop“ (do nothing).

From now on his user program must be no longer with z.Bsp. Avrdude to upload the controller, otherwise the bootloader would be deleted / overwritten. It now loads the user program z.Bsp. the boot uploader to the controller. Once this is done, it looks in the store then like this:

Spppppppppppppppppppppppppp--------------|-----|xxxxx|xxx|x--e
                                         :     :     :   :
                                         :     :     :   +FIRSTBOOTSTART
                                         :     :     +SeCONDBOOTSTART
                                         :     +-THIRDBOOTSTART
                                         +FOURTHBOOTSTART
S = start of flash memory
e = ende of flash memory
| = bootloader borders
x = bootloader programm
p = user programm

If one wants to implement from the user program the function „Firmware supports reset command“ in the boat uploader, we must not jump to address 0x0000 (happens with Luna command reset) with the command to restart. This would even start only the user program again, nothing else. it is necessary for the „reset“ support in the boot uploader only to jump to the bootloader: jump THIRD BOOT START so this is also carried out.

Example

This example contains the boot loader, adapted for the Atmega168, as well as a matching user program. The controller board then requires a serial connection to the PC. The necessary to be activated fuse bits are: [x] BOOTRST
[x] BOOTSZ0
[x] BOOTSZ1
The execution start point (or bootloader address) in this case is then FOURTHBOOTSTART.

method

  • Read fusebits and enable (checked) above fusebits.
  • Write fusebits.
  • Compile bootloader and upload into the controller.
  • Test program only compile.
  • boat uploader start, select compiled test program, set the baud rate and interface & Connect.
  • With „Upload Flash“ upload the test program.

Files

bootloader-m168.luna
'******************************************************************************
' Author      : rgf, avr@myluna.de, http://avr.myluna.de
' Title       : bootloader example
' Last updated: 09.02.2015
' Protocol    : v1.0
' Description : flash and eeprom write with crc check, retry and error handling
' Tools       : Uploader software "bootUploader" http://avr.myluna.de
'******************************************************************************
' Compiler    : LunaAVR 2015r1 or newer
'******************************************************************************
avr.device = atmega168
avr.clock = 20000000
avr.stack = 64
 
#library "Library/Crc8.interface"
'******************************************************************************
'*** CHANGE CODE START ADDRESS TO BOOT LOADER SECTION *************************
'******************************************************************************
'Luna Bootloader requires 2 kb bootloader section
avr.CodeStartAddr	= nrww_start_addr
const MAX_PAGES		= nrww_start_addr/pagesize
'******************************************************************************
 
uart.baud = 19200
uart.Recv.enable
uart.Send.enable
 
#define SLED as portd.6			'status led
#define WLED as portd.7			'write action led
 
SLED.mode = output,low		'status led
WLED.mode = output,low		'write led
 
 
'******************************************************************************
'luna bootloader protocol (universal)
'******************************************************************************
' input commands
const PROTO_RDY		= 0x01		'access bootloader and keep alive when accessed
const PROTO_CBV		= 0x02		'read controller base values, answer:
const PROTO_EFP		= 0x04		'erase flash page, command: <word:pagenum>, answer: <ack>
const PROTO_WFP		= 0x05		'erase and write flash page, command: <word:pagenum><bytes:data><byte:crc8>, answer: <ack>
const PROTO_EEP		= 0x06		'erase eeprom page, command: <word:pagenum>, answer: <ack>
const PROTO_WEP		= 0x07		'erase and write eeprom page, command: <word:pagenum><bytes:data><byte:crc8>, answer: <ack>
'	long	flashsize_bytes
'	long	bootloaderStartAddr_bytes
'	word	eepromSize_bytes
'	word	pagesizeFlash_bytes
'	word	pagesizeEeprom_bytes
'	byte	crc8
'	byte	<ack>
const PROTO_END		= 0xff		'end of processing, reset controller
' input/output commands
const PROTO_ACK		= 0xfd		'acknowledge
const PROTO_NAK		= 0xfe		'not acknowledge
' memory types
const MEMTYPE_FLASH		= 0
const MEMTYPE_EEPROM	= 1
' misc values
const COMMAND_TIMEOUT	= 10000				'command timeout in ms
const PAGE_BYTES		= PageSize*2		'page size flash in bytes
const PAGE_BYTES_EE		= 8					'page size eeprom in bytes (do not change!)
 
 
'******************************************************************************
'******************************************************************************
' main program
'******************************************************************************
dim i,cnt,cmd,buffer(PAGE_BYTES-1),type as byte
dim ctcnt,page,eadr as word
dim rtcnt as long
 
main()
 
procedure main()
  if CheckBootloaderAccess() then
    ack()
    do
      cmd=uart.GetByte
      if cmd then
        clr ctcnt
        select case cmd
        case PROTO_RDY
          ack()
        case PROTO_CBV
          uart.writelong avr.flashsize
          uart.writelong avr.CodeStartAddr*2
          uart.writeword avr.eramsize
          uart.writeword PAGE_BYTES
          uart.writeword PAGE_BYTES_EE
          crc8.Start
          crc8.AddLong avr.flashsize
          crc8.AddLong avr.CodeStartAddr*2
          crc8.AddWord avr.eramsize
          crc8.AddWord PAGE_BYTES
          crc8.AddWord PAGE_BYTES_EE
          uart.WriteByte crc8.value
          ack()
        case PROTO_EFP
          ack()
          type=MEMTYPE_FLASH
          ErasePage()
        case PROTO_WFP
          ack()
          type=MEMTYPE_FLASH
          EraseWritePage()
        case PROTO_EEP
          ack()
          type=MEMTYPE_EEPROM
          ErasePage()
        case PROTO_WEP
          ack()
          type=MEMTYPE_EEPROM
          EraseWritePage()
        case PROTO_END
          reset
        default
          clr ctcnt
          nak()
        end select
      end if
      delay(1)
      incr ctcnt
    loop until ctcnt=COMMAND_TIMEOUT
  end if
  'jump to main program
  reset
endproc
 
'******************************************************************************
'******************************************************************************
'sub routines
'******************************************************************************
'******************************************************************************
procedure delay(a as byte)
  waitms a
endproc
 
'******************************************************************************
'check if bootloader called
function CheckBootloaderAccess() as byte
  for i=0 to 9
    SLED.toggle
    if uart.GetByte=PROTO_RDY then
      return true
    end if
    delay(50)
  next
  SLED = 1
  delay(1000)
  SLED = 0
  return false
endfunc
 
'******************************************************************************
'******************************************************************************
'erase/write page
'******************************************************************************
'******************************************************************************
procedure ErasePage()
  page=uart.readword
  if MAX_PAGES>=page then  'prevent overwriting bootloader section
    decr page									'page number for writing is zero-based
    WLED=1
    if type=MEMTYPE_FLASH then
      for i=0 To buffer().Ubound
        buffer(i) = 0xff
      next
      flash.PageWrite buffer(),page			    'write the page data to flash
    else
      eadr=page*PAGE_BYTES_EE
      for i=0 To PAGE_BYTES_EE-1
        eeprom.bytevalue(eadr) = 0xff
        incr eadr
      next
    endif
    ack()
    WLED=0
    return
  end if
  nak()
endproc
 
procedure EraseWritePage()
  page=uart.readword
  if MAX_PAGES>=page then  'prevent overwriting bootloader section
    crc8.start									'initialize crc calculation
    crc8.AddWord page							'add page number to crc stream
    if type=MEMTYPE_FLASH then
      cnt=buffer().Ubound
    else
      cnt=PAGE_BYTES_EE-1
    end if
    for i=0 To cnt								'read page data
      buffer(i) = uart.ReadByte				  'add to buffer
      crc8.AddByte buffer(i)					  'add to crc stream
    next
    if uart.ReadByte = crc8.value then			'equate received and calculated crc
      decr page								  'page number for writing is zero-based
      WLED=1									  'indicate write
      if type=MEMTYPE_FLASH then
        flash.PageWrite buffer(),page			  'write the page data to flash
      else
        eadr=page*PAGE_BYTES_EE
        for i=0 To PAGE_BYTES_EE-1
          eeprom.bytevalue(eadr) = buffer(i)
          incr eadr
        next
      endif
      WLED=0
      ack()
      return
    end if
  end if
  nak()
endproc
 
procedure ack()
  uart.WriteByte PROTO_ACK				'say OK to transmitter
endproc
procedure nak()
  uart.WriteByte PROTO_NAK				'failed
endproc
testprogramm.luna
'
'Testprogramm fuer Bootloader-Aufrufe aus dem Programm heraus
'
'compiler:
'---------
'luna 2015r1 or newer
'
'history:
'--------
'2013-02-12 demel start
'2015-02-09 Anpassung an 2015r1 or newer
'
'references:
'-----------
'Luna Bootloader:         http://avr.myluna.de/doku.php?id=de:bootloader
'TaskMgr:               http://avr.myluna.de/doku.php?id=de:taskmgr
'ATmega88/168/328/32 doku:    http://www.atmel.com
'LunaAVR doku:           http://avr.myluna.de/doku.php?id=de:download
'
 
avr.device = atmega168
avr.clock = 20000000
avr.stack = 64
 
#library "Library/TaskTimer.interface"
#library "Library/TaskMgr.interface"
 
const USE_SERIAL = 1
 
'Die Baudrate muss mit der im Bootloader uebereinstimmen!!
#if USE_SERIAL
  uart.baud = 19200
  uart.Recv.enable
  uart.Send.enable
#endif
 
'Je nach Kontroller START_BOOTLOADER und die Fuses anpassen
#if avr.device = "atmega88"
  const START_BOOTLOADER   = FOURTHBOOTSTART
#endif
#if avr.device = "atmega168"
  const START_BOOTLOADER   = FOURTHBOOTSTART
#endif
#if avr.device = "atmega328p"
  const START_BOOTLOADER   = THIRDBOOTSTART
#endif
#if avr.device = "atmega32"
  const START_BOOTLOADER   = THIRDBOOTSTART
#endif
 
#define STATUS_LED as portd.5   'Status LED gruen, nur zum testen
 
STATUS_LED.mode = output,low
 
'Benutzereingabe RS232 Konsole
Dim chars as string
 
part TaskManager_initialisieren
  'Task usw. fuer TaskMgr definieren
  #define TIMED_TASKS		as 2			'2 Tasks im TaskTimer
  #define TIMED_TIMESLICE	as 1	 		'TaskTimer lauft mit 1 ms
  #define MAXIMUM_TASKS 	as 2			'2 Tasks im TaskManager
 
  const FLASH_TIME1   = 150      '150 * 1ms = 150ms
 
  'TaskMgr initialisieren
  TaskMgr.Init(MAXIMUM_TASKS)
  TaskMgr.TaskAdd(  task_led1_toggle().Addr, FLASH_TIME1)
  'TaskMgr.TaskAdd( task_led_gruen_toggle().Addr, FLASH_TIME2)
 
  'TaskTimer initialisieren mit 1 Task und einem Intervall von 1 ms.
  TaskTimer.Init(Timer2, TIMED_TASKS, TIMED_TIMESLICE) '1 Tasks, 1 ms
 
  'TaskTimer-ISR soll nur die selbst veraenderten Register sichern.
  'Dies weil wir eine Methode aus einer Library setzen, welche ihrerseits ebenfalls
  'ihre veraenderten Register sichert. Dies spart Stack-Speicher.
  TaskTimer.SaveMode = selfused
 
  'Die PollRoutine des TaskMgr in den TaskTimer eintragen
  TaskTimer.Task(0) = TaskMgr.Poll().Addr
endpart
 
' Enable all Interrupt
Avr.Interrupts.Enable
 
#if USE_SERIAL
  print "Test Bootloader"
  print avr.device
#endif
 
do
  'Konsolenfunktion fuer Bootloader-Bearbeitung
  'hier koennen bei Bedarf natuerlich noch mehr Befehle stehen.
  Print "Bootloader Demo > "
  chars = uart.InpStr()
 
  select case chars
  case "reset"
    jump START_BOOTLOADER
  case "cls"
    print 12;
  case "help"
    call help()
  default
    print "Kein gueltiges Komamndo"
  endselect
loop
 
idle
  'Eigenen Programmcode hier einfuegen
  if TaskMgr.Tick then                  '1ms Takt vom TaskManager
    TaskMgr.Schedule()               	'Scheduler aufrufen
  endif
endidle
 
procedure task_led1_toggle()
  STATUS_LED.toggle
  taskmgr.TaskAdd( task_led1_toggle().Addr, FLASH_TIME1 )
endproc
 
procedure Help()
  print " Kommando            Funktion"
  print "--------------------------------------------"
  print " help                Befehlsuebersicht"
  print " cls                 Bildschirm loeschen"
  print " reset               Neustart durchfuehren"
  print
endproc