123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692 |
- #define FUNC_READ 1
- #define FUNC_WRITE 1
- /**********************************************************/
- /* Optiboot bootloader for Arduino */
- /* */
- /* http://optiboot.googlecode.com */
- /* */
- /* Arduino-maintained version : See README.TXT */
- /* http://code.google.com/p/arduino/ */
- /* It is the intent that changes not relevant to the */
- /* Arduino production environment get moved from the */
- /* optiboot project to the arduino project in "lumps." */
- /* */
- /* Heavily optimised bootloader that is faster and */
- /* smaller than the Arduino standard bootloader */
- /* */
- /* Enhancements: */
- /* Fits in 512 bytes, saving 1.5K of code space */
- /* Higher baud rate speeds up programming */
- /* Written almost entirely in C */
- /* Customisable timeout with accurate timeconstant */
- /* Optional virtual UART. No hardware UART required. */
- /* Optional virtual boot partition for devices without. */
- /* */
- /* What you lose: */
- /* Implements a skeleton STK500 protocol which is */
- /* missing several features including EEPROM */
- /* programming and non-page-aligned writes */
- /* High baud rate breaks compatibility with standard */
- /* Arduino flash settings */
- /* */
- /* Supported Devices: */
- /* With the merges of Spence Konde's ATTinyCore */
- /* https://github.com/SpenceKonde/ATTinyCore */
- /* and MCUDude's MightyCore, MiniCore, MegaCore, etc */
- /* https://github.com/MCUdude */
- /* the number of supported chips and configurations */
- /* has become quite large. Try "make help" for a list */
- /* Not all chips have received the same amount of */
- /* overall attention. Some version may have bugs. */
- /* */
- /* Does not support: */
- /* USB based devices (eg. Teensy, Leonardo) */
- /* */
- /* Assumptions: */
- /* The code makes several assumptions that reduce the */
- /* code size. They are all true after a hardware reset, */
- /* but may not be true if the bootloader is called by */
- /* other means or on other hardware. */
- /* No interrupts can occur */
- /* UART and Timer 1 are set to their reset state */
- /* SP points to RAMEND */
- /* */
- /* Code builds on code, libraries and optimisations from: */
- /* stk500boot.c by Jason P. Kyle */
- /* Arduino bootloader http://arduino.cc */
- /* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */
- /* avr-libc project http://nongnu.org/avr-libc */
- /* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */
- /* AVR305 Atmel Application Note */
- /* */
- /* Copyright 2013-2021 by Bill Westfield. */
- /* Copyright 2010 by Peter Knight. */
- /* Note that many others have made major contributions! */
- /* */
- /* This program is free software; you can redistribute it */
- /* and/or modify it under the terms of the GNU General */
- /* Public License as published by the Free Software */
- /* Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will */
- /* be useful, but WITHOUT ANY WARRANTY; without even the */
- /* implied warranty of MERCHANTABILITY or FITNESS FOR A */
- /* PARTICULAR PURPOSE. See the GNU General Public */
- /* License for more details. */
- /* */
- /* You should have received a copy of the GNU General */
- /* Public License along with this program; if not, write */
- /* to the Free Software Foundation, Inc., */
- /* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
- /* */
- /* Licence can be viewed at */
- /* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */
- /* */
- /**********************************************************/
- /**********************************************************/
- /* */
- /* Optional defines: */
- /* */
- /**********************************************************/
- /* */
- /* BIGBOOT: */
- /* Build a 1k bootloader, not 512 bytes. This turns on */
- /* extra functionality; notably EEPROM */
- /* */
- /* NO_APP_SPM: */
- /* omit the code that allows applications to "call" the */
- /* bootloader to perform SPM operations. */
- /* */
- /* SUPPORT_EEPROM: */
- /* Support reading and writing from EEPROM. This is not */
- /* used by Arduino, so off by default. */
- /* */
- /* WDTTIME: */
- /* Bootloader timeout period, in seconds. */
- /* 1 and 2 seconds are available on all chips */
- /* 4 and 8 are supported on most */
- /* */
- /* NO_START_APP_ON_POR: */
- /* If NO_START_APP_ON_POR is set, the bootloader will */
- /* also run on power-on */
- /* */
- /* START_APP_ON_EXTR: */
- /* If START_APP_ON_EXTR is set, the bootloader will not */
- /* run in the event of an external reset. */
- /* */
- /* If both are set, the bootloader will only run if the */
- /* app jumps directly to it. */
- /* */
- /* ---------- Comm features */
- /* UART: */
- /* UART number (0..n) for devices with more than */
- /* one hardware uart (644P, 1284P, etc) */
- /* */
- /* BAUD_RATE: */
- /* Set bootloader baud rate. */
- /* */
- /* SINGLESPEED: */
- /* do not use "U2X" mode for UART initialization. This */
- /* can be useful for some bit rates. */
- /* */
- /* SOFT_UART: */
- /* Use AVR305 soft-UART instead of hardware UART. */
- /* */
- /* RS485: */
- /* Either a pin (ex: "B5") or a pin followed by _INV */
- /* (ex: "B5_INV") which indicates inverted polarity. */
- /* This pin will be held low while transmitting in order */
- /* to control the direction pin of an RS485 tranciever */
- /* (RS485 support by Vladimir Dronnikov (github.com/dvv) */
- /* as used in github.com/SodaqMoja/optiboot ) */
- /* */
- /* ---------- LED behavior */
- /* LED: */
- /* Which pin to use for the LED flashs. This is a pin */
- /* name like "B5" */
- /* */
- /* LED_START_FLASHES: */
- /* Number of LED flashes on bootup. */
- /* */
- /* LED_DATA_FLASH: */
- /* Flash LED when transferring data. For boards without */
- /* TX or RX LEDs, or for people who like blinky lights. */
- /* */
- /* LED_START_ON: */
- /* Instead of blinking, turn the LED on when we start, */
- /* and off when it exits. Slightly smaller code. */
- /* */
- /**********************************************************/
- /*
- * default values.
- */
- #if !defined(BIGBOOT)
- # define BIGBOOT 0
- #endif
- #if !defined(SUPPORT_EEPROM)
- # define SUPPORT_EEPROM 0
- #endif
- #if !defined(APP_NOSPM)
- # define APP_NOSPM 0
- #endif
- #if !defined(START_APP_ON_EXTR)
- # define START_APP_ON_EXTR 0
- #endif
- #if !defined(NO_START_APP_ON_POR)
- # define NO_START_APP_ON_POR 0
- #endif
- /* UART options */
- #if !defined(SOFT_UART)
- # define SOFT_UART 0
- #endif
- #if !defined(UART)
- #define UART 0
- #endif
- #if !defined(SINGLESPEED)
- #define SINGLESPEED 0
- #endif
- /* LED Options */
- #if !defined(LED_START_FLASHES)
- #define LED_START_FLASHES 0
- #endif
- #if !defined(LED_DATA_FLASH)
- # define LED_DATA_FLASH 0
- #endif
- #if !defined(LED_START_ON)
- # define LED_START_ON 0
- #endif
- /**********************************************************/
- /* Version Numbers! */
- /* */
- /* Arduino Optiboot now includes this Version number in */
- /* the source and object code. */
- /* */
- /* Version 3 was released as zip from the optiboot */
- /* repository and was distributed with Arduino 0022. */
- /* Version 4 starts with the arduino repository commit */
- /* that brought the arduino repository up-to-date with */
- /* the optiboot source tree changes since v3. */
- /* Version 5 was created at the time of the new Makefile */
- /* structure (Mar, 2013), even though no binaries changed*/
- /* Version 6 added EEPROM support, including causing an */
- /* error when trying to write eeprom with versions that */
- /* didn't have the code there. Makefiles were further */
- /* restructured. Overlapping SPM/download removed. */
- /* Version 7 straightened out the MCUSR and RESET */
- /* handling, did MORE Makefile mods. EEPROM support now */
- /* fits in 512 bytes, if you turn off LED Blinking. */
- /* Various bigboot and virboot targets were fixed. */
- /* Version 8.0 adds the do_spm code callable from Apps. */
- /* */
- /* It would be good if versions implemented outside the */
- /* official repository used an out-of-seqeunce version */
- /* number (like 104.6 if based on based on 4.5) to */
- /* prevent collisions. The CUSTOM_VERSION=n option */
- /* adds n to the high version to facilitate this. */
- /* */
- /**********************************************************/
- /**********************************************************/
- /* Edit History: */
- /* */
- /* Oct 2021 */
- /* 8.3 WestfW add section-start determination code to */
- /* the C source. In theory, this causes no changes */
- /* to the binary, but it's "risky", so ... bump. */
- /* Aug 2019 */
- /* 8.1 WestfW Fix bug in calculation of Vboot offset */
- /* Sep 2018 */
- /* 8.0 WestfW (and Majekw and MCUDude) */
- /* Include do_spm routine callable from the app */
- /* at BOOTSTART+2, controllable with compile option */
- /* July 2018 */
- /* 7.0 WestfW (with much input from Others) */
- /* Fix MCUSR treatement as per much discussion, */
- /* Patches by MarkG55, majekw. Preserve value */
- /* for the application, as much as possible. */
- /* see https://github.com/Optiboot/optiboot/issues/97 */
- /* Optimize a bit by implementing a union for the */
- /* various 16bit address values used (based on */
- /* observation by "aweatherguy", but different.) */
- /* Slightly optimize math in VIRTUAL_BOOT code */
- /* Add some virboot targets, fix some fuses. */
- /* Implement LED_START_ON; less code than flashes */
- /* Aug 2014 */
- /* 6.2 WestfW: make size of length variables dependent */
- /* on the SPM_PAGESIZE. This saves space */
- /* on the chips where it's most important. */
- /* 6.1 WestfW: Fix OPTIBOOT_CUSTOMVER (send it!) */
- /* Make no-wait mod less picky about */
- /* skipping the bootloader. */
- /* Remove some dead code */
- /* Jun 2014 */
- /* 6.0 WestfW: Modularize memory read/write functions */
- /* Remove serial/flash overlap */
- /* (and all references to NRWWSTART/etc) */
- /* Correctly handle pagesize > 255bytes */
- /* Add EEPROM support in BIGBOOT (1284) */
- /* EEPROM write on small chips now causes err */
- /* Split Makefile into smaller pieces */
- /* Add Wicked devices Wildfire */
- /* Move UART=n conditionals into pin_defs.h */
- /* Remove LUDICOUS_SPEED option */
- /* Replace inline assembler for .version */
- /* and add OPTIBOOT_CUSTOMVER for user code */
- /* Fix LED value for Bobuino (Makefile) */
- /* Make all functions explicitly inline or */
- /* noinline, so we fit when using gcc4.8 */
- /* Change optimization options for gcc4.8 */
- /* Make ENV=arduino work in 1.5.x trees. */
- /* May 2014 */
- /* 5.0 WestfW: Add support for 1Mbps UART */
- /* Mar 2013 */
- /* 5.0 WestfW: Major Makefile restructuring. */
- /* See Makefile and pin_defs.h */
- /* (no binary changes) */
- /* */
- /* 4.6 WestfW/Pito: Add ATmega32 support */
- /* 4.6 WestfW/radoni: Don't set LED_PIN as an output if */
- /* not used. (LED_START_FLASHES = 0) */
- /* Jan 2013 */
- /* 4.6 WestfW/dkinzer: use autoincrement lpm for read */
- /* 4.6 WestfW/dkinzer: pass reset cause to app in R2 */
- /* Mar 2012 */
- /* 4.5 WestfW: add infrastructure for non-zero UARTS. */
- /* 4.5 WestfW: fix SIGNATURE_2 for m644 (bad in avr-libc) */
- /* Jan 2012: */
- /* 4.5 WestfW: fix NRWW value for m1284. */
- /* 4.4 WestfW: use attribute OS_main instead of naked for */
- /* main(). This allows optimizations that we */
- /* count on, which are prohibited in naked */
- /* functions due to PR42240. (keeps us less */
- /* than 512 bytes when compiler is gcc4.5 */
- /* (code from 4.3.2 remains the same.) */
- /* 4.4 WestfW and Maniacbug: Add m1284 support. This */
- /* does not change the 328 binary, so the */
- /* version number didn't change either. (?) */
- /* June 2011: */
- /* 4.4 WestfW: remove automatic soft_uart detect (didn't */
- /* know what it was doing or why.) Added a */
- /* check of the calculated BRG value instead. */
- /* Version stays 4.4; existing binaries are */
- /* not changed. */
- /* 4.4 WestfW: add initialization of address to keep */
- /* the compiler happy. Change SC'ed targets. */
- /* Return the SW version via READ PARAM */
- /* 4.3 WestfW: catch framing errors in getch(), so that */
- /* AVRISP works without HW kludges. */
- /* http://code.google.com/p/arduino/issues/detail?id=368n*/
- /* 4.2 WestfW: reduce code size, fix timeouts, change */
- /* verifySpace to use WDT instead of appstart */
- /* 4.1 WestfW: put version number in binary. */
- /**********************************************************/
- #define OPTIBOOT_MAJVER 8
- #define OPTIBOOT_MINVER 3
- /*
- * OPTIBOOT_CUSTOMVER should be defined (by the makefile) for custom edits
- * of optiboot. That way you don't wind up with very different code that
- * matches the version number of a "released" optiboot.
- */
- #if !defined(OPTIBOOT_CUSTOMVER)
- #define OPTIBOOT_CUSTOMVER 0
- #endif
- unsigned const int __attribute__((section(".version")))
- optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
- #define __AVR_LIBC_DEPRECATED_ENABLE__ 1 // don't poison MCUSR on some chips
- #include <inttypes.h>
- #include <avr/io.h>
- #include <avr/pgmspace.h>
- #include <avr/eeprom.h>
- /*
- * optiboot uses several "address" variables that are sometimes byte pointers,
- * sometimes word pointers. sometimes 16bit quantities, and sometimes built
- * up from 8bit input characters. avr-gcc is not great at optimizing the
- * assembly of larger words from bytes, but we can use the usual union to
- * do this manually. Expanding it a little, we can also get rid of casts.
- */
- typedef union {
- uint8_t *bptr;
- uint16_t *wptr;
- uint16_t word;
- uint8_t bytes[2];
- } addr16_t;
- /*
- * Note that we use a replacement of "boot.h"
- * <avr/boot.h> uses sts instructions, but this version uses out instructions
- * This saves cycles and program memory, if possible.
- * boot_opt.h pulls in the standard boot.h for the odd target (?)
- */
- #include "boot_opt.h"
- // We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need.
- /*
- * pin_defs.h
- * This contains most of the rather ugly defines that implement our
- * ability to use UART=n and LED=D3, and some avr family bit name differences.
- */
- #include "pin_defs.h"
- /*
- * stk500.h contains the constant definitions for the stk500v1 comm protocol
- */
- #include "stk500.h"
- /* set the UART baud rate defaults */
- #ifndef BAUD_RATE
- #if F_CPU >= 8000000L
- #define BAUD_RATE 115200L // Highest rate Avrdude win32 will support
- #elif F_CPU >= 1000000L
- #define BAUD_RATE 9600L // 19200 also supported, but with significant error
- #elif F_CPU >= 128000L
- #define BAUD_RATE 4800L // Good for 128kHz internal RC
- #else
- #define BAUD_RATE 1200L // Good even at 32768Hz
- #endif
- #endif
- #if (SOFT_UART == 0)
- #if SINGLESPEED
- /* Single speed option */
- #define BAUD_SETTING (( (F_CPU + BAUD_RATE * 8L) / ((BAUD_RATE * 16L))) - 1 )
- #define BAUD_ACTUAL (F_CPU/(16 * ((BAUD_SETTING)+1)))
- #else
- /* Normal U2X usage */
- #define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 )
- #define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1)))
- #endif
- #if BAUD_ACTUAL <= BAUD_RATE
- #define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE)
- #if BAUD_ERROR >= 5
- #error BAUD_RATE off by greater than -5%
- #elif BAUD_ERROR >= 2 && !defined(PRODUCTION)
- #warning BAUD_RATE off by greater than -2%
- #endif
- #else
- #define BAUD_ERROR (( 100*(BAUD_ACTUAL - BAUD_RATE) ) / BAUD_RATE)
- #if BAUD_ERROR >= 5
- #error BAUD_RATE off by greater than 5%
- #elif BAUD_ERROR >= 2 && !defined(PRODUCTION)
- #warning BAUD_RATE off by greater than 2%
- #endif
- #endif
- #if BAUD_SETTING > 250
- #error Unachievable baud rate (too slow) BAUD_RATE
- #endif // baud rate slow check
- #if (BAUD_SETTING - 1) < 3
- #if BAUD_ERROR != 0 // permit high bitrates (ie 1Mbps@16MHz) if error is zero
- #error Unachievable baud rate (too fast) BAUD_RATE
- #endif
- #endif // baud rate fast check
- #endif // SOFT_UART
- /* Watchdog settings */
- #define WATCHDOG_OFF (0)
- #define WATCHDOG_16MS (_BV(WDE))
- #define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
- #define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
- #define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
- #define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
- #define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
- #define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
- #define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
- #ifdef WDP3
- #define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
- #define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
- #endif
- /*
- * Watchdog timeout translations from human readable to config vals
- */
- #ifndef WDTTIME
- # define WDTPERIOD WATCHDOG_1S // 1 second default
- #elif WDTTIME == 1
- # define WDTPERIOD WATCHDOG_1S // 1 second
- #elif WDTTIME == 2
- # define WDTPERIOD WATCHDOG_2S // 2 seconds
- #elif defined(WDP3) && (WDTTIME == 4)
- # define WDTPERIOD WATCHDOG_4S // 4 seconds
- #elif defined(WDP3) && (WDTTIME == 8)
- # define WDTPERIOD WATCHDOG_8S // 8 seconds
- #else
- #error Invalid TIMEOUT
- #endif
- /*
- * We can never load flash with more than 1 page at a time, so we can save
- * some code space on parts with smaller pagesize by using a smaller int.
- */
- #if SPM_PAGESIZE > 255
- typedef uint16_t pagelen_t ;
- #define GETLENGTH(len) len = getch()<<8; len |= getch()
- #else
- typedef uint8_t pagelen_t;
- #define GETLENGTH(len) (void) getch() /* skip high byte */; len = getch()
- #endif
- /* Function Prototypes
- * The main() function is in init9, which removes the interrupt vector table
- * we don't need. It is also 'OS_main', which means the compiler does not
- * generate any entry or exit code itself (but unlike 'naked', it doesn't
- * suppress some compile-time options we want.)
- */
- void pre_main(void) __attribute__ ((naked)) __attribute__ ((section (".init8")));
- int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9"))) __attribute__((used));
- void __attribute__((noinline)) __attribute__((leaf)) putch(char);
- uint8_t __attribute__((noinline)) __attribute__((leaf)) getch(void) ;
- void __attribute__((noinline)) verifySpace();
- void __attribute__((noinline)) watchdogConfig(uint8_t x);
- static void getNch(uint8_t);
- #if LED_START_FLASHES > 0
- static inline void flash_led(uint8_t);
- #endif
- static inline void watchdogReset();
- static inline void writebuffer(int8_t memtype, addr16_t mybuff,
- addr16_t address, pagelen_t len);
- static inline void read_mem(uint8_t memtype,
- addr16_t, pagelen_t len);
- #if SOFT_UART
- void uartDelay() __attribute__ ((naked));
- #endif
- /*
- * RAMSTART should be self-explanatory. It's bigger on parts with a
- * lot of peripheral registers. Let 0x100 be the default
- * Note that RAMSTART (for optiboot) need not be exactly at the start of RAM.
- */
- #if !defined(RAMSTART) // newer versions of gcc avr-libc define RAMSTART
- #define RAMSTART 0x100
- #if defined (__AVR_ATmega644P__)
- // correct for a bug in avr-libc
- #undef SIGNATURE_2
- #define SIGNATURE_2 0x0A
- #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
- #undef RAMSTART
- #define RAMSTART (0x200)
- #endif
- #endif
- /* C zero initialises all global variables. However, that requires */
- /* These definitions are NOT zero initialised, but that doesn't matter */
- /* This allows us to drop the zero init code, saving us memory */
- static addr16_t buff = {(uint8_t *)(RAMSTART)};
- /* Virtual boot partition support */
- #ifdef VIRTUAL_BOOT_PARTITION
- // RAM locations to save vector info (temporarilly)
- #define rstVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+4))
- #define rstVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+5))
- #define saveVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+6))
- #define saveVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+7))
- #define RSTVEC_ADDRESS 0 // flash address where vectors start.
- // Vector to save original reset jump:
- // SPM Ready is least probably used, so it's default
- // if not, use old way WDT_vect_num,
- // or simply set custom save_vect_num in Makefile using vector name
- // or even raw number.
- #if !defined (save_vect_num)
- #if defined (SPM_RDY_vect_num)
- #define save_vect_num (SPM_RDY_vect_num)
- #elif defined (SPM_READY_vect_num)
- #define save_vect_num (SPM_READY_vect_num)
- #elif defined (EE_RDY_vect_num)
- #define save_vect_num (EE_RDY_vect_num)
- #elif defined (EE_READY_vect_num)
- #define save_vect_num (EE_READY_vect_num)
- #elif defined (WDT_vect_num)
- #define save_vect_num (WDT_vect_num)
- #else
- #error "Can't find SPM or WDT interrupt vector for this CPU"
- #endif
- #endif //save_vect_num
- // check if it's on the same page (code assumes that)
- #if FLASHEND > 8192
- // AVRs with more than 8k of flash have 4-byte vectors, and use jmp.
- // We save only 16 bits of address, so devices with more than 128KB
- // may behave wrong for upper part of address space.
- #define rstVect0 2
- #define rstVect1 3
- #define saveVect0 (save_vect_num*4+2)
- #define saveVect1 (save_vect_num*4+3)
- #define appstart_vec (save_vect_num*2)
- // address of page that will have the saved vector
- #define SAVVEC_ADDRESS (SPM_PAGESIZE*(save_vect_num/(SPM_PAGESIZE/4)))
- #else
- // AVRs with up to 8k of flash have 2-byte vectors, and use rjmp.
- #define rstVect0 0
- #define rstVect1 1
- #define saveVect0 (save_vect_num*2)
- #define saveVect1 (save_vect_num*2+1)
- #define appstart_vec (save_vect_num)
- // address of page that will have the saved vector
- #define SAVVEC_ADDRESS (SPM_PAGESIZE*(save_vect_num/(SPM_PAGESIZE/2)))
- #endif
- #else
- #define appstart_vec (0)
- #endif // VIRTUAL_BOOT_PARTITION
- void sectionOpts() __attribute__((naked));
- void sectionOpts() {
- /*
- * Mysterious Magic code to allow section-start addresses to be
- * specifed by the C code (which, after all, has access to CPP symbols
- * like FLASHEND, and can make a "safer" guess than putting manually
- * determined constants in the Makefile. This is dependent on a custom
- * linker script that makes use of the __BOOT_ symbols that we define.
- * Fortunately, that script can be used to link optiboot on any of its
- * usual chips.
- *
- * with some judicious use of asm directives, we can define symbols
- * that the linker will look at for positioning our segments (in
- * combination with a custom linker script) based on the flash size
- * defined via io.h, instead of requiring magic --section-start
- * constants on the build line.
- *
- * This apparently needs to be inside a function in order to pass
- * arguments to the asm command (even though the asm doesn't
- * generate any code) But we can make sure the function (which doesn't
- * actually contain any code) doesn't actually end up in the binary.
- */
- #if BIGBOOT
- /*
- * BIGBOOT images are easy, because they're much smaller than the
- * flash size that they are defined to occupy (1k)
- */
- asm(" .global __BOOT_SIZE__, __BOOT_START__, __VERSION_START__\n"
- " .equ __BOOT_SIZE__, 1024\n"
- " .equ __BOOT_START__, (%0-1023)\n"
- " .equ __VERSION_START__, (%0-1)\n"
- ::"i"((uint32_t)FLASHEND));
- #else
- /*
- * non-BIGBOOT images try to fit in 512bytes, but don't quite fit
- * on chips that don't have HW support for a boot section. We try
- * to guess how big the extra bits of code are, and then round up
- * to the next larger flash page boundry (which can be nearly 4 pages
- * away on chips with FOURPAGEERASE)
- * These numbers are determined manually by building a reference
- * implementation with various options and comparing sizes.
- */
- # if VIRTUAL_BOOT_PARTITION
- /* VIRTUAL_BOOT_PARTITION code is about 100 bytes */
- # define VBSIZE 110
- # else
- # define VBSIZE 0
- # endif
- # if LED_START_FLASHES==0
- /* if LED_START_FLASHES is 0 (eg on 8pin chips), it saves some code */
- # define LEDSIZE (-30)
- # else
- # define LEDSIZE 0
- # endif
- # if SOFT_UART
- /* But not having a hardware UART costs a bit */
- # define SOFTUSIZE 16
- # else
- # define SOFTUSIZE 0
- # endif
- # if FOURPAGEERASE
- /* FOURPAGEERASE adds some code, and also affects how we round */
- # define ERASESIZE 8
- # define PAGSIZ_B (SPM_PAGESIZE*4)
- # else
- # define PAGSIZ_B (SPM_PAGESIZE)
- # define ERASESIZE 0
- # endif
- /*
- * Round our guess up to next page size. So 560 byte images occupy 576bytes,
- * 600 byte images occupy 640 bytes, etc. Depending on chip.
- * Note that SPM_PAGESIZE is already in bytes in the .h files.
- */
- # define ROUNDTOPAGE(size) (((size + (PAGSIZ_B/2))/(PAGSIZ_B))*(PAGSIZ_B))
- /* use 511 here so we don't round up to the next page on normal systems. */
- # define BOOTSIZE (511+ERASESIZE+VBSIZE+SOFTUSIZE+LEDSIZE)
- asm(" .global __BOOT_SIZE__, __BOOT_START__, __VERSION_START__\n"
- " .equ __BOOT_SIZE__, %1\n"
- " .equ __BOOT_START__, ((%0+1)-%1)\n"
- " .equ __VERSION_START__, (%0-1)\n"
- ::"i"((uint32_t)FLASHEND), "i"(ROUNDTOPAGE(BOOTSIZE)));
- #endif
- /* Wasn't that FUN! */
- }
- /* everything that needs to run VERY early */
- void pre_main(void) {
- // Allow convenient way of calling do_spm function - jump table, so
- // entry to this function will always be here, independent of
- // compilation, features etc
- asm volatile (
- " rjmp 1f\n"
- #if APP_NOSPM
- " ret\n" // if do_spm isn't include, return without doing anything
- #else
- " rjmp do_spm\n"
- #endif
- "1:\n"
- );
- }
- /* main program starts here */
- void bootloader(unsigned char check_reset) {
- uint8_t ch;
- /*
- * Making these local and in registers prevents the need for initializing
- * them, and also saves space because code no longer stores to memory.
- * (initializing address keeps the compiler happy, but isn't really
- * necessary, and uses 4 bytes of flash.)
- */
- register addr16_t address;
- register pagelen_t length;
- // After the zero init loop, this is the first code to run.
- //
- // This code makes the following assumptions:
- // No interrupts will execute
- // SP points to RAMEND
- // r1 contains zero
- //
- // If not, uncomment the following instructions:
- // cli();
- asm volatile (" clr __zero_reg__");
- #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8515__) || \
- defined(__AVR_ATmega8535__) || defined (__AVR_ATmega16__) || \
- defined (__AVR_ATmega32__) || defined (__AVR_ATmega64__) || \
- defined (__AVR_ATmega128__) || defined (__AVR_ATmega162__)
- SP=RAMEND; // This is done by hardware reset on newer chips
- #endif
- #if defined(OSCCAL_VALUE)
- OSCCAL = OSCCAL_VALUE;
- #endif
- /*
- * Protect as much from MCUSR as possible for application
- * and still skip bootloader if not necessary
- *
- * Code by MarkG55
- * see discussion in https://github.com/Optiboot/optiboot/issues/97
- */
- #ifndef MCUSR // Backward compatability with old AVRs
- #define MCUSR MCUCSR
- #endif
- #if (START_APP_ON_EXTR == 0 && NO_START_APP_ON_POR == 0)
- /*
- * Normal behavior. Start if EXTRF in on and WDRF is off
- */
- # define APP_START_REASONS ((ch & (_BV(WDRF) | _BV(EXTRF))) != _BV(EXTRF))
- # define WDRF_CLR_REASONS (ch & _BV(EXTRF))
- #elif (START_APP_ON_EXTR && NO_START_APP_ON_POR)
- /*
- * If NO_START_APP_ON_POR is defined, run bootloader after POR.
- * This allows use with reset disabled: power on and immediately
- * program.
- */
- # define APP_START_REASONS ((ch & (_BV(PORF) | _BV(WDRF) | _BV(EXTRF))) != _BV(PORF))
- # define WDRF_CLR_REASONS ((ch & (_BV(PORF) | _BV(WDRF))) == (_BV(PORF) | _BV(WDRF)))
- #elif ((START_APP_ON_EXTR == 0) && NO_START_APP_ON_POR)
- /*
- * If START_APP_ON_EXTR is defined, don't run bootloader after
- * an external reset - only useful in combination with above,
- * for some unusual use cases.
- */
- # define APP_START_REASONS (ch & _BV(WDRF))
- # define WDRF_CLR_REASONS (ch & (_BV(PORF) | _BV(EXTRF)))
- #else
- /*
- * Only run bootloader via jmp to 0 with MCUSR==0.
- * Probably an error.
- */
- # warning "Bootloader can only start via app request because "
- # warning "START_APP_ON_EXTR is defined and NO_START_APP_ON_POR isn't"
- # define APP_START_REASONS 1 /* Always start rge App. */
- # define WDRF_CLR_REASONS 0 /* Never clear WDRF */
- #endif
- ch = MCUSR;
- // Skip all logic and run bootloader if MCUSR is cleared (application request)
- if (check_reset && ch != 0) {
- /*
- * To run the boot loader, External Reset Flag must be set.
- * If not, we could make shortcut and jump directly to application code.
- * Also WDRF set with EXTRF is a result of Optiboot timeout, so we
- * shouldn't run bootloader in loop :-) That's why:
- * 1. application is running if WDRF is cleared
- * 2. we clear WDRF if it's set with EXTRF to avoid loops
- * One problematic scenario: broken application code sets watchdog timer
- * without clearing MCUSR before and triggers it quickly. But it's
- * recoverable by power-on with pushed reset button.
- */
- if (APP_START_REASONS) {
- if (WDRF_CLR_REASONS) {
- /*
- * Clear WDRF if it was most probably set by wdr in bootloader.
- */
- if ((uint16_t)&MCUSR > 0x1F) {
- MCUSR = ~(_BV(WDRF)); // optimize to LDI/OUT
- } else {
- MCUSR &= ~(_BV(WDRF)); // or optimize to CBI if possible
- }
- }
- /*
- * save the reset flags in the designated register
- * This can be saved in a main program by putting code in .init0 (which
- * executes before normal c init code) to save R2 to a global variable.
- */
- __asm__ __volatile__ (" mov r2, %0\n" :: "r" (ch));
- // switch off watchdog
- watchdogConfig(WATCHDOG_OFF);
- // Note that appstart_vec is defined so that this works with either
- // real or virtual boot partitions.
- __asm__ __volatile__ (
- // Jump to 'save' or RST vector
- #ifdef VIRTUAL_BOOT_PARTITION
- // full code version for virtual boot partition
- " ldi r30,%[rstvec]\n"
- " clr r31\n"
- " ijmp\n"::[rstvec] "M"(appstart_vec)
- #else
- #ifdef RAMPZ
- // use absolute jump for devices with lot of flash
- " jmp 0\n"::
- #else
- // use rjmp to go around end of flash to address 0
- // it uses fact that optiboot_version constant is 2 bytes before end of flash
- " rjmp optiboot_version+2\n"
- #endif //RAMPZ
- #endif //VIRTUAL_BOOT_PARTITION
- );
- }
- } //end handling of MCUSR !=0
- #if LED_START_FLASHES > 0
- // Set up Timer 1 for timeout counter
- #if defined(__AVR_ATtiny261__)||defined(__AVR_ATtiny461__)||defined(__AVR_ATtiny861__)
- TCCR1B = 0x0E; //div 8196 - we could divide by less since it's a 10-bit counter, but why?
- #elif defined(__AVR_ATtiny25__)||defined(__AVR_ATtiny45__)||defined(__AVR_ATtiny85__)
- TCCR1 = 0x0E; //div 8196 - it's an 8-bit timer.
- #elif defined(__AVR_ATtiny43__)
- #error "LED flash for Tiny43 not yet supported"
- #else
- TCCR1B = _BV(CS12) | _BV(CS10); // div 1024
- #endif
- #endif
- #if (SOFT_UART == 0)
- #if defined(__AVR_ATmega8__) || defined (__AVR_ATmega8515__) || \
- defined (__AVR_ATmega8535__) || defined (__AVR_ATmega16__) || \
- defined (__AVR_ATmega32__)
- #if (SINGLESPEED == 0)
- UCSRA = _BV(U2X); //Double speed mode USART
- #endif //singlespeed
- UCSRB = _BV(RXEN) | _BV(TXEN); // enable Rx & Tx
- UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // config USART; 8N1
- UBRRL = (uint8_t)BAUD_SETTING;
- #else // mega8/etc
- #ifdef LIN_UART
- //DDRB|=3;
- LINCR = (1 << LSWRES);
- //LINBRRL = (((F_CPU * 10L / 32L / BAUD_RATE) + 5L) / 10L) - 1;
- LINBRRL=(uint8_t)BAUD_SETTING;
- LINBTR = (1 << LDISR) | (8 << LBT0);
- LINCR = _BV(LENA) | _BV(LCMD2) | _BV(LCMD1) | _BV(LCMD0);
- LINDAT=0;
- #else
- #if (SINGLESPEED == 0)
- UART_SRA = _BV(U2X0); //Double speed mode USART0
- #endif
- UART_SRB = _BV(RXEN0) | _BV(TXEN0);
- UART_SRC = _BV(UCSZ00) | _BV(UCSZ01);
- UART_SRL = (uint8_t)BAUD_SETTING;
- #endif // LIN_UART
- #endif // mega8/etc
- #endif // soft_uart
- #ifdef RS485
- RS485_DDR |= _BV(RS485_BIT);
- #ifdef RS485_INVERT
- RS485_PORT |= _BV(RS485_BIT);
- #else
- RS485_PORT &= ~_BV(RS485_BIT);
- #endif
- #endif
- // Set up watchdog to trigger after desired timeout
- watchdogConfig(WDTPERIOD);
- #if (LED_START_FLASHES > 0) || LED_DATA_FLASH || LED_START_ON
- /* Set LED pin as output */
- LED_DDR |= _BV(LED);
- #endif
- /* hold POWER_ON (GPStracker) */
- DDRA |= _BV(PA3);
- PORTA |= _BV(PA3);
- #if SOFT_UART
- /* Set TX pin as HIGH output */
- UART_PORT |= _BV(UART_TX_BIT);
- UART_DDR |= _BV(UART_TX_BIT);
- #endif
- #if LED_START_FLASHES > 0
- /* Flash onboard LED to signal entering of bootloader */
- flash_led(LED_START_FLASHES * 2);
- #else
- #if LED_START_ON
- /* Turn on LED to indicate starting bootloader (less code!) */
- LED_PORT |= _BV(LED);
- #endif
- #endif
- /* Forever loop: exits by causing WDT reset */
- for (;;) {
- /* get character from UART */
- ch = getch();
- // putch(ch);
- /*continue;*/
- if (ch == STK_GET_SYNC) {
- verifySpace();
- }
- else if (ch == STK_GET_PARAMETER) {
- unsigned char which = getch();
- verifySpace();
- /*
- * Send optiboot version as "SW version"
- * Note that the references to memory are optimized away.
- */
- if (which == STK_SW_MINOR) {
- putch(optiboot_version & 0xFF);
- } else if (which == STK_SW_MAJOR) {
- putch(optiboot_version >> 8);
- } else {
- /*
- * GET PARAMETER returns a generic 0x03 reply for
- * other parameters - enough to keep Avrdude happy
- */
- putch(0x03);
- }
- }
- else if (ch == STK_SET_DEVICE) {
- // SET DEVICE is ignored
- getNch(20);
- }
- else if (ch == STK_SET_DEVICE_EXT) {
- // SET DEVICE EXT is ignored
- getNch(5);
- }
- else if (ch == STK_LOAD_ADDRESS) {
- // LOAD ADDRESS
- address.bytes[0] = getch();
- address.bytes[1] = getch();
- #ifdef RAMPZ
- // Transfer top bit to LSB in RAMPZ
- if (address.bytes[1] & 0x80) {
- RAMPZ |= 0x01;
- }
- else {
- RAMPZ &= 0xFE;
- }
- #endif
- address.word *= 2; // Convert from word address to byte address
- verifySpace();
- }
- else if (ch == STK_UNIVERSAL) {
- #ifdef RAMPZ
- // LOAD_EXTENDED_ADDRESS is needed in STK_UNIVERSAL for addressing more than 128kB
- if ( AVR_OP_LOAD_EXT_ADDR == getch() ) {
- // get address
- getch(); // get '0'
- RAMPZ = (RAMPZ & 0x01) | ((getch() << 1) & 0xff); // get address and put it in RAMPZ
- getNch(1); // get last '0'
- // response
- putch(0x00);
- }
- else {
- // everything else is ignored
- getNch(3);
- putch(0x00);
- }
- #else
- // UNIVERSAL command is ignored
- getNch(4);
- putch(0x00);
- #endif
- }
- /* Write memory, length is big endian and is in bytes */
- else if (ch == STK_PROG_PAGE) {
- // PROGRAM PAGE - we support flash programming only, not EEPROM
- uint8_t desttype;
- uint8_t *bufPtr;
- pagelen_t savelength;
- GETLENGTH(length);
- savelength = length;
- desttype = getch();
- // read a page worth of contents
- bufPtr = buff.bptr;
- do *bufPtr++ = getch();
- while (--length);
- // Read command terminator, start reply
- verifySpace();
- #ifdef VIRTUAL_BOOT_PARTITION
- /*
- * How the Virtual Boot Partition works:
- * At the beginning of a normal AVR program are a set of vectors that
- * implement the interrupt mechanism. Each vector is usually a single
- * instruction that dispatches to the appropriate ISR.
- * The instruction is normally an rjmp (on AVRs with 8k or less of flash)
- * or jmp instruction, and the 0th vector is executed on reset and jumps
- * to the start of the user program:
- * vectors: jmp startup
- * jmp ISR1
- * jmp ISR2
- * : ;; etc
- * jmp lastvector
- * To implement the "Virtual Boot Partition", Optiboot detects when the
- * flash page containing the vectors is being programmed, and replaces the
- * startup vector with a jump to te beginning of Optiboot. Then it saves
- * the applications's startup vector in another (must be unused by the
- * application), and finally programs the page with the changed vectors.
- * Thereafter, on reset, the vector will dispatch to the beginning of
- * Optiboot. When Optiboot decides that it will run the user application,
- * it fetches the saved start address from the unused vector, and jumps
- * there.
- * The logic is dependent on size of flash, and whether the reset vector is
- * on the same flash page as the saved start address.
- */
- #if FLASHEND > 8192
- /*
- * AVR with 4-byte ISR Vectors and "jmp"
- * WARNING: this works only up to 128KB flash!
- */
- #if FLASHEND > (128*1024)
- #error "Can't use VIRTUAL_BOOT_PARTITION with more than 128k of Flash"
- #endif
- if (address.word == RSTVEC_ADDRESS) {
- // This is the reset vector page. We need to live-patch the
- // code so the bootloader runs first.
- //
- // Save jmp targets (for "Verify")
- rstVect0_sav = buff.bptr[rstVect0];
- rstVect1_sav = buff.bptr[rstVect1];
- // Add "jump to Optiboot" at RESET vector
- // WARNING: this works as long as 'main' is in first section
- buff.bptr[rstVect0] = ((uint16_t)pre_main) & 0xFF;
- buff.bptr[rstVect1] = ((uint16_t)pre_main) >> 8;
- #if (SAVVEC_ADDRESS != RSTVEC_ADDRESS)
- // the save_vector is not necessarilly on the same flash page as the reset
- // vector. If it isn't, we've waiting to actually write it.
- } else if (address.word == SAVVEC_ADDRESS) {
- // Save old values for Verify
- saveVect0_sav = buff.bptr[saveVect0 - SAVVEC_ADDRESS];
- saveVect1_sav = buff.bptr[saveVect1 - SAVVEC_ADDRESS];
- // Move RESET jmp target to 'save' vector
- buff.bptr[saveVect0 - SAVVEC_ADDRESS] = rstVect0_sav;
- buff.bptr[saveVect1 - SAVVEC_ADDRESS] = rstVect1_sav;
- }
- #else
- // Save old values for Verify
- saveVect0_sav = buff.bptr[saveVect0];
- saveVect1_sav = buff.bptr[saveVect1];
- // Move RESET jmp target to 'save' vector
- buff.bptr[saveVect0] = rstVect0_sav;
- buff.bptr[saveVect1] = rstVect1_sav;
- }
- #endif
- #else // bigger flash
- #ifdef KeepBracesMatched
- {
- #endif
- /*
- * AVR with 2-byte ISR Vectors and rjmp
- */
- if (address.word == rstVect0) {
- // This is the reset vector page. We need to live-patch
- // the code so the bootloader runs first.
- //
- // Move RESET vector to 'save' vector
- // Save jmp targets (for "Verify")
- rstVect0_sav = buff.bptr[rstVect0];
- rstVect1_sav = buff.bptr[rstVect1];
- addr16_t vect;
- vect.word = ((uint16_t)pre_main-1);
- // Instruction is a relative jump (rjmp), so recalculate.
- // an RJMP instruction is 0b1100xxxx xxxxxxxx, so we should be able to
- // do math on the offsets without masking it off first.
- // Note that rjmp is relative to the already incremented PC, so the
- // offset is one less than you might expect.
- buff.bptr[0] = vect.bytes[0]; // rjmp to start of bootloader
- buff.bptr[1] = vect.bytes[1] | 0xC0; // make an "rjmp"
- #if (SAVVEC_ADDRESS != RSTVEC_ADDRESS)
- } else if (address.word == SAVVEC_ADDRESS) {
- addr16_t vect;
- vect.bytes[0] = rstVect0_sav;
- vect.bytes[1] = rstVect1_sav;
- // Save old values for Verify
- saveVect0_sav = buff.bptr[saveVect0 - SAVVEC_ADDRESS];
- saveVect1_sav = buff.bptr[saveVect1 - SAVVEC_ADDRESS];
- vect.word = (vect.word-save_vect_num); //substract 'save' interrupt position
- // Move RESET jmp target to 'save' vector
- buff.bptr[saveVect0 - SAVVEC_ADDRESS] = vect.bytes[0];
- buff.bptr[saveVect1 - SAVVEC_ADDRESS] = (vect.bytes[1] & 0x0F)| 0xC0; // make an "rjmp"
- }
- #else
- #ifdef KeepBracesMatched
- {
- #endif
- // Save old values for Verify
- saveVect0_sav = buff.bptr[saveVect0];
- saveVect1_sav = buff.bptr[saveVect1];
- vect.bytes[0] = rstVect0_sav;
- vect.bytes[1] = rstVect1_sav;
- vect.word = (vect.word-save_vect_num); //substract 'save' interrupt position
- // Move RESET jmp target to 'save' vector
- buff.bptr[saveVect0] = vect.bytes[0];
- buff.bptr[saveVect1] = (vect.bytes[1] & 0x0F)| 0xC0; // make an "rjmp"
- // Add rjmp to bootloader at RESET vector
- vect.word = ((uint16_t)pre_main-1); // (main) is always <= 0x0FFF; no masking needed.
- buff.bptr[0] = vect.bytes[0]; // rjmp 0x1c00 instruction
- }
- #endif
- #endif // FLASHEND
- #endif // VBP
- writebuffer(desttype, buff, address, savelength);
- }
- /* Read memory block mode, length is big endian. */
- else if (ch == STK_READ_PAGE) {
- uint8_t desttype;
- GETLENGTH(length);
- desttype = getch();
- verifySpace();
- read_mem(desttype, address, length);
- }
- /* Get device signature bytes */
- else if (ch == STK_READ_SIGN) {
- // READ SIGN - return what Avrdude wants to hear
- verifySpace();
- putch(SIGNATURE_0);
- putch(SIGNATURE_1);
- putch(SIGNATURE_2);
- }
- else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
- // Adaboot no-wait mod
- watchdogConfig(WATCHDOG_16MS);
- verifySpace();
- }
- else {
- // This covers the response to commands like STK_ENTER_PROGMODE
- verifySpace();
- }
- putch(STK_OK);
- }
- }
- int main(void) {
- bootloader(1);
- }
- __attribute__((section(".manualcall"))) void manual_call(void) {
- bootloader(0);
- }
- void putch(char ch) {
- #if (SOFT_UART == 0)
- // Hardware UARTs.
- #ifndef LIN_UART
- #ifdef RS485
- uint8_t x;
- do {
- x = UART_SRA;
- } while (!(x & _BV(UDRE0)));
- // clear transmitted flag
- x |= _BV(TXC0);
- UART_SRA = x;
- // put transceiver to output mode
- # ifdef RS485_INVERT
- RS485_PORT &= ~_BV(RS485_BIT);
- # else
- RS485_PORT |= _BV(RS485_BIT);
- # endif
- // put char
- UART_UDR = ch;
- // wait for char transmitted
- while (!(UART_SRA & _BV(TXC0))) { /* Spin */ }
- // put transceiver to input mode
- # ifdef RS485_INVERT
- RS485_PORT |= _BV(RS485_BIT);
- # else
- RS485_PORT &= ~_BV(RS485_BIT);
- # endif
- #else //not RS485
- while (!(UART_SRA & _BV(UDRE0))) { /* Spin */ }
- UART_UDR = ch;
- #endif
- #else //is LIN UART
- while (!(LINSIR & _BV(LTXOK))) { /* Spin */ }
- UART_UDR = ch;
- #endif
- #else // SOFT_UART
- #ifdef RS485
- // put transceiver to output mode
- #ifdef RS485_INVERT
- RS485_PORT &= ~_BV(RS485_BIT);
- #else
- RS485_PORT |= _BV(RS485_BIT);
- #endif
- #endif
- __asm__ __volatile__ (
- " com %[ch]\n" // ones complement, carry set
- " sec\n"
- "1: brcc 2f\n"
- " cbi %[uartPort],%[uartBit]\n"
- " rjmp 3f\n"
- "2: sbi %[uartPort],%[uartBit]\n"
- " nop\n"
- "3: rcall uartDelay\n"
- " rcall uartDelay\n"
- " lsr %[ch]\n"
- " dec %[bitcnt]\n"
- " brne 1b\n"
- :
- :
- [bitcnt] "d" (10),
- [ch] "r" (ch),
- [uartPort] "I" (_SFR_IO_ADDR(UART_PORT)),
- [uartBit] "I" (UART_TX_BIT)
- :
- "r25"
- );
- #ifdef RS485
- // put transceiver to input mode
- #ifdef RS485_INVERT
- RS485_PORT |= _BV(RS485_BIT);
- #else
- RS485_PORT &= ~_BV(RS485_BIT);
- #endif
- #endif
- #endif // SOFT_UART
- }
- static void inline toggle_led(void) {
- #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8515__) || \
- defined(__AVR_ATmega8535__) || defined(__AVR_ATmega16__) || \
- defined(__AVR_ATmega162__) || defined(__AVR_ATmega32__) || \
- defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
- LED_PORT ^= _BV(LED);
- #else
- LED_PIN |= _BV(LED); // Newer AVRs can toggle by writing PINx
- #endif
- }
- uint8_t getch(void) {
- uint8_t ch;
- #if LED_DATA_FLASH
- toggle_led();
- #endif
- #if SOFT_UART
- watchdogReset();
- __asm__ __volatile__ (
- "1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge
- " rjmp 1b\n"
- " rcall uartDelay\n" // Get to middle of start bit
- "2: rcall uartDelay\n" // Wait 1 bit period
- " rcall uartDelay\n" // Wait 1 bit period
- " clc\n"
- " sbic %[uartPin],%[uartBit]\n"
- " sec\n"
- " dec %[bitCnt]\n"
- " breq 3f\n"
- " ror %[ch]\n"
- " rjmp 2b\n"
- "3:\n"
- :
- [ch] "=r" (ch)
- :
- [bitCnt] "d" (9),
- [uartPin] "I" (_SFR_IO_ADDR(UART_PIN)),
- [uartBit] "I" (UART_RX_BIT)
- :
- "r25"
- );
- #else
- #ifndef LIN_UART
- while (!(UART_SRA & _BV(RXC0))) { /* Spin */ }
- if (!(UART_SRA & _BV(FE0))) {
- /*
- * A Framing Error indicates (probably) that something is talking
- * to us at the wrong bit rate. Assume that this is because it
- * expects to be talking to the application, and DON'T reset the
- * watchdog. This should cause the bootloader to abort and run
- * the application "soon", if it keeps happening. (Note that we
- * don't care that an invalid char is returned...)
- */
- watchdogReset();
- }
- #else
- while (!(LINSIR & _BV(LRXOK))) { /* Spin */ }
- if (!(LINSIR & _BV(LFERR))) {
- watchdogReset(); /* Eventually abort if wrong speed */
- }
- #endif
- ch = UART_UDR;
- #endif
- #if LED_DATA_FLASH
- toggle_led();
- #endif
- return ch;
- }
- #if SOFT_UART
- // AVR305 equation: #define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6)
- // Adding 3 to numerator simulates nearest rounding for more accurate baud rates
- #define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6)
- #if UART_B_VALUE > 255
- #error Baud rate too slow for soft UART
- #endif
- #if UART_B_VALUE < 6
- // (this value is a "guess" at when loop/call overhead might become too
- // significant for the soft uart to work. It tests OK with the popular
- // "ATtinycore" chips that need SOFT_UART, at the usual clock/baud combos.)
- #error Baud rate too high for soft UART
- #endif
- void uartDelay() {
- __asm__ __volatile__ (
- " ldi r25,%[count]\n"
- "1: dec r25\n"
- " brne 1b\n"
- " ret\n"
- ::[count] "M" (UART_B_VALUE)
- );
- }
- #endif
- void getNch(uint8_t count) {
- do getch(); while (--count);
- verifySpace();
- }
- void verifySpace() {
- if (getch() != CRC_EOP) {
- watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
- while (1) // and busy-loop so that WD causes
- ; // a reset and app start.
- }
- putch(STK_INSYNC);
- }
- #if LED_START_FLASHES > 0
- void flash_led(uint8_t count) {
- do {
- #if defined(__AVR_ATtiny261__)||defined(__AVR_ATtiny461__)||defined(__AVR_ATtiny861__) || defined(__AVR_ATtiny25__)||defined(__AVR_ATtiny45__)||defined(__AVR_ATtiny85__)
- TCNT1 = 0xFF & (-(F_CPU/(8196L*16)));
- TIFR = _BV(TOV1);
- while (!(TIFR & _BV(TOV1)));
- #elif defined(__AVR_ATtiny43__)
- #error "LED flash for Tiny43 not yet supported"
- #else
- TCNT1 = -(F_CPU/(1024*16));
- TIFR1 = _BV(TOV1);
- while (!(TIFR1 & _BV(TOV1)));
- #endif
- toggle_led();
- watchdogReset();
- #if (SOFT_UART == 0)
- /*
- * While in theory, the STK500 initial commands would be buffered
- * by the UART hardware, avrdude sends several attempts in rather
- * quick succession, some of which will be lost and cause us to
- * get out of sync. So if we see any data; stop blinking.
- */
- #ifndef LIN_UART
- if (UART_SRA & _BV(RXC0))
- #else
- if (LINSIR & _BV(LRXOK))
- #endif
- break;
- #else
- // This doesn't seem to work?
- // if ((UART_PIN & (1<<UART_RX_BIT)) == 0)
- // break; // detect start bit on soft uart too.
- #endif
- } while (--count);
- }
- #endif
- // Watchdog functions. These are only safe with interrupts turned off.
- void watchdogReset() {
- __asm__ __volatile__ (
- " wdr\n"
- );
- }
- void watchdogConfig(uint8_t x) {
- #ifdef WDCE //does it have a Watchdog Change Enable?
- #ifdef WDTCSR
- WDTCSR = _BV(WDCE) | _BV(WDE);
- #else
- WDTCR= _BV(WDCE) | _BV(WDE);
- #endif
- #else //then it must be one of those newfangled ones that use CCP
- CCP=0xD8; //so write this magic number to CCP
- #endif
- #ifdef WDTCSR
- WDTCSR = x;
- #else
- WDTCR= x;
- #endif
- }
- /*
- * void writebuffer(memtype, buffer, address, length)
- */
- static inline void writebuffer(int8_t memtype, addr16_t mybuff,
- addr16_t address, pagelen_t len)
- {
- switch (memtype) {
- case 'E': // EEPROM
- #if SUPPORT_EEPROM || BIGBOOT
- while (len--) {
- eeprom_write_byte((address.bptr++), *(mybuff.bptr++));
- }
- #else
- /*
- * On systems where EEPROM write is not supported, just busy-loop
- * until the WDT expires, which will eventually cause an error on
- * host system (which is what it should do.)
- */
- while (1)
- ; // Error: wait for WDT
- #endif
- break;
- default: // FLASH
- /*
- * Default to writing to Flash program memory. By making this
- * the default rather than checking for the correct code, we save
- * space on chips that don't support any other memory types.
- */
- {
- // Copy buffer into programming buffer
- uint16_t addrPtr = address.word;
- /*
- * Start the page erase and wait for it to finish. There
- * used to be code to do this while receiving the data over
- * the serial link, but the performance improvement was slight,
- * and we needed the space back.
- */
- #ifdef FOURPAGEERASE
- if ((address.bytes[0] & ((SPM_PAGESIZE<<2)-1))==0) {
- #endif
- __boot_page_erase_short(address.word);
- boot_spm_busy_wait();
- #ifdef FOURPAGEERASE
- }
- #endif
- /*
- * Copy data from the buffer into the flash write buffer.
- */
- do {
- __boot_page_fill_short((uint16_t)(void*)addrPtr, *(mybuff.wptr++));
- addrPtr += 2;
- } while (len -= 2);
- /*
- * Actually Write the buffer to flash (and wait for it to finish.)
- */
- __boot_page_write_short(address.word);
- boot_spm_busy_wait();
- #if defined(RWWSRE)
- // Reenable read access to flash
- __boot_rww_enable_short();
- #endif
- } // default block
- break;
- } // switch
- }
- static inline void read_mem(uint8_t memtype, addr16_t address, pagelen_t length)
- {
- uint8_t ch;
- switch (memtype) {
- #if SUPPORT_EEPROM || BIGBOOT
- case 'E': // EEPROM
- do {
- putch(eeprom_read_byte((address.bptr++)));
- } while (--length);
- break;
- #endif
- default:
- do {
- #ifdef VIRTUAL_BOOT_PARTITION
- // Undo vector patch in bottom page so verify passes
- if (address.word == rstVect0) ch = rstVect0_sav;
- else if (address.word == rstVect1) ch = rstVect1_sav;
- else if (address.word == saveVect0) ch = saveVect0_sav;
- else if (address.word == saveVect1) ch = saveVect1_sav;
- else ch = pgm_read_byte_near(address.bptr);
- address.bptr++;
- #elif defined(RAMPZ)
- // Since RAMPZ should already be set, we need to use EPLM directly.
- // Also, we can use the autoincrement version of lpm to update "address"
- // do putch(pgm_read_byte_near(address++));
- // while (--length);
- // read a Flash and increment the address (may increment RAMPZ)
- __asm__ (" elpm %0,Z+\n" : "=r" (ch), "=z" (address.bptr): "1" (address));
- #else
- // read a Flash byte and increment the address
- __asm__ (" lpm %0,Z+\n" : "=r" (ch), "=z" (address.bptr): "1" (address));
- #endif
- putch(ch);
- } while (--length);
- break;
- } // switch
- }
- #if (APP_NOSPM == 0)
- /*
- * Separate function for doing spm stuff
- * It's needed for application to do SPM, as SPM instruction works only
- * from bootloader.
- *
- * How it works:
- * - do SPM
- * - wait for SPM to complete
- * - if chip have RWW/NRWW sections it does additionaly:
- * - if command is WRITE or ERASE, AND data=0 then reenable RWW section
- *
- * In short:
- * If you play erase-fill-write, just set data to 0 in ERASE and WRITE
- * If you are brave, you have your code just below bootloader in NRWW section
- * you could do fill-erase-write sequence with data!=0 in ERASE and
- * data=0 in WRITE
- */
- static void do_spm(uint16_t address, uint8_t command, uint16_t data) __attribute__ ((used));
- static void do_spm(uint16_t address, uint8_t command, uint16_t data) {
- // Do spm stuff
- asm volatile (
- " movw r0, %3\n"
- " __wr_spmcsr %0, %1\n"
- " spm\n"
- " clr r1\n"
- :
- : "i" (_SFR_MEM_ADDR(__SPM_REG)),
- "r" ((uint8_t)command),
- "z" ((uint16_t)address),
- "r" ((uint16_t)data)
- : "r0"
- );
- // wait for spm to complete
- // it doesn't have much sense for __BOOT_PAGE_FILL,
- // but it doesn't hurt and saves some bytes on 'if'
- boot_spm_busy_wait();
- #if defined(RWWSRE)
- // this 'if' condition should be: (command == __BOOT_PAGE_WRITE || command == __BOOT_PAGE_ERASE)...
- // but it's tweaked a little assuming that in every command we are interested in here, there
- // must be also SELFPRGEN set. If we skip checking this bit, we save here 4B
- if ((command & (_BV(PGWRT)|_BV(PGERS))) && (data == 0) ) {
- // Reenable read access to flash
- __boot_rww_enable_short();
- }
- #endif
- }
- #endif
- #if BIGBOOT
- /*
- * Optiboot is designed to fit in 512 bytes, with a minimum feature set.
- * Some chips have a minimum bootloader size of 1024 bytes, and sometimes
- * it is desirable to add extra features even though 512bytes is exceedded.
- * In that case, the BIGBOOT can be used.
- * Our extra features so far don't come close to filling 1k, so we can
- * add extra "frivolous" data to the image. In particular, we can add
- * information about how Optiboot was built (which options were selected,
- * what version, all in human-readable form (and extractable from the
- * binary with avr-strings.)
- *
- * This can always be removed or trimmed if more actual program space
- * is needed in the future. Currently the data occupies about 160 bytes,
- */
- #define xstr(s) str(s)
- #define str(s) #s
- __attribute__((section(".fini9"))) const char f_delimit = 0xFF;
- #define OPTFLASHSECT __attribute__((section(".fini8")))
- #define OPT2FLASH(o) OPTFLASHSECT const char f##o[] = #o "=" xstr(o)
- #if LED_START_FLASHES
- OPT2FLASH(LED_START_FLASHES);
- #endif
- #if LED_DATA_FLASH
- OPT2FLASH(LED_DATA_FLASH);
- #endif
- #if LED_START_ON
- OPT2FLASH(LED_START_ON);
- #endif
- #ifdef LED_NAME
- OPTFLASHSECT const char f_LED[] = "LED=" LED_NAME;
- #endif
- #if SUPPORT_EEPROM
- OPT2FLASH(SUPPORT_EEPROM);
- #endif
- #if defined(RS485)
- OPTFLASHSECT const char f_rs485[] = "RS485=" RS485_NAME;
- #endif
- #if BAUD_RATE
- OPT2FLASH(BAUD_RATE);
- #endif
- #if SOFT_UART
- OPT2FLASH(SOFT_UART);
- #endif
- #if defined(UART)
- OPT2FLASH(UART);
- #endif
- #if (!defined(NODATE)) || (NODATE == 0)
- // Leave out the date, useful if we want to compare binaries
- OPTFLASHSECT const char f_date[] = "Built:" __DATE__ ":" __TIME__;
- #endif
- #if BIGBOOT
- OPT2FLASH(BIGBOOT);
- #endif
- #ifdef VIRTUAL_BOOT_PARTITION
- OPTFLASHSECT const char f_boot[] = "Virtual_Boot_Partition";
- #endif
- OPT2FLASH(F_CPU);
- OPTFLASHSECT const char f_device[] = "Device=" xstr(__AVR_DEVICE_NAME__);
- #if OPTIBOOT_CUSTOMVER
- OPT2FLASH(OPTIBOOT_CUSTOMVER);
- #endif
- OPTFLASHSECT const char f_version[] = "Version=" xstr(OPTIBOOT_MAJVER) "." xstr(OPTIBOOT_MINVER);
- #endif
|