Files
bia_microddr_fw/bootloader_hacked_from_mla/src/main.c

627 lines
29 KiB
C
Raw Normal View History

/*******************************************************************************
Copyright 2016 Microchip Technology Inc. (www.microchip.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
To request to license the code under the MLA license (www.microchip.com/mla_license),
please contact mla_licensing@microchip.com
*******************************************************************************/
/*********************************************************************
IMPORTANT NOTES: This code can be readily adapted for use with the
both the F and LF versions of the following devices:
PIC18F4553/4458/2553/2458
PIC18F4550/4455/2550/2455
PIC18F4450/2450
PIC18F14K50/13K50
PIC18F45K50/25K50/24K50
However, the default device that is currently selected in the project
may not be the device you are interested. To change the device:
Replace the linker script with an appropriate version, and
click "Configure --> Select Device" and select the proper
microcontroller. Also double check to verify that the HardwareProfile.h and
usb_config.h are properly configured to match your desired application
platform.
Verify that the configuration bits are set correctly for the intended
target application, and fix any build errors that result from either
the #error directives, or due to I/O pin count mismatch issues (such
as when using a 28-pin device, but without making sufficient changes
to the HardwareProfile.h file)
-------------------------------------------------------------------
NOTE FOR BUILDING WITH C18 COMPILER:
-------------------------------------------------------------------
This project needs to be built with the full compiler optimizations
enabled, and using the Default storage class "Static" or the total
code size will be too large to fit within the program memory
range 0x000-0xFFF. The default linker script included
in the project has this range reserved for the use by the bootloader,
but marks the rest of program memory as "PROTECTED". If you try to
build this project with the compiler optimizations turned off, or
you try to modify some of this code, but add too much code to fit
within the 0x000-0xFFF region, a linker error like that below may occur:
Error - section '.code' can not fit the section. Section '.code' length=0x00000020
To fix this error, either optimize the program to fit within 0x000-0xFFF
(such as by turning on all compiler optimizations, and making sure the
"default storage class" is set to "Static"), or modify the linker
and vector remapping (as well as the application projects) to allow this
bootloader to use more program memory.
-------------------------------------------------------------------
NOTE FOR BUILDING THIS BOOTLOADER FIRMWARE WITH THE XC8 COMPILER:
-------------------------------------------------------------------
To build this code with the XC8 compiler, make sure to use the linker
option:
ROM ranges: default,-1000-XXXXXX //Where "XXXXXX" is the last implemented flash memory
address, excluding the config bit region (ex: "default,-1000-7FFF" for the 32kB PIC18F45K50)
This setting is found in the XC8 compiler linker settings, Option category: Memory Model.
If any errors are encountered relating to "cannot find space", this
presumably means that either the compiler was configured to build the
code in Free or Standard mode, or that modifications have been made to
the code that have increased the code size to exceed the 0x000-0xFFF program
memory region. If this error is encountered, make sure to build the project
with all PRO mode optimizations enabled, and/or optimize any user added code
that is causing the project size to exceed the 0x000-0xFFF region.
----------------------------------------------------------------------
NOTE FOR BUILDING APPLICATION FIRMWARE PROJECTS WITH THE XC8 COMPILER:
----------------------------------------------------------------------
When building the application project that is meant to be programmed by this
bootloader, you must use different XC8 linker settings as this project.
For application projects, two linker settings are required:
ROM ranges: default,-0-FFF,-1006-1007,-1016-1017
Codeoffset: 0x1000
When the above settings are implemented, the application firmware will no longer work
without the bootloader present. Therefore, it is recommended to add the bootloader
firmware output (from this bootloader project) .hex file as a "Loadable" into
the application firmware project. This will allow the "HEXMATE" tool to run
after building the application firmware project, which will merge the application
output .hex file contents with the bootloader output .hex file contents (which was
added as a loadable file).
However, in some cases you may encounter build errors during the hex merge operation.
This will occur if there are any overlapping regions in the bootloader firmware
.hex file, with the application firmware .hex file, when the contents of these
overlapping regions are not 100% exact matches to each other. Normally, only the
configuration bit settings will be overlapping between the two projects.
Therefore, to prevent build errors, the configuration bit settings between the
bootloader firmware project and the application firmware project must be set to
100% exactly identical values (or they must only be set in one of the projects,
to eliminate the overlapping region altogether).
----------------------Bootloader Entry------------------------------------------
Entry into this bootloader firmware can be done by either of two possible
ways:
1. I/O pin check at power up/after any reset. and/or:
2. Software entry via absolute jump to address 0x001C.
The I/O pin check method is the most rugged, since it does not require the
application firmware image to be intact (at all) to get into the bootloader
mode. However, software entry is also possible and may be more convenient
in applications that do not have user exposed pushbuttons available.
When the "application" image is executing, it may optionally jump into
bootloader mode, by executing a _asm goto 0x001C _endasm instruction.
Before doing so however, the firwmare should configure the current
clock settings to be compatible with USB module operation, in they
are not already. Once the goto 0x001C has been executed the USB device
will detach from the USB bus (if it was previously attached), and will
re-enumerate as a HID class device with a new VID/PID (adjustable via
usb_dsc.c settings), which can communicate with the associated
USB host software that loads and programs the new .hex file.
--------------------------------------------------------------------------------
Anytime that an application implements flash self erase/write capability,
special care should be taken to make sure that the microcontroller is operated
within all datasheet ratings, especially those associated with voltage versus
frequency.
Operating the device at too high of a frequency (for a given voltage, ex: by
operating at 48MHz at 2.1V, while the device datasheet indicates some higher
value such as 2.35V+ is required) can cause unexpected code operation. This
could potentially allow inadvertent execution of bootloader or other self
erase/write routines, causing corruption of the flash memory of the application.
To avoid this, all applications that implement self erase/write capability
should make sure to prevent execution during overclocked/undervolted conditions.
For this reason, enabling and using the microcontroller hardware Brown-out-Reset
feature is particularly recommended for applications using a bootloader. If
BOR is not used, or the trip threshold is too low for the intended application
frequency, it is suggested to add extra code in the application to detect low
voltage conditions, and to intentionally clock switch to a lower frequency
(or put the device to sleep) during the low voltage condition. Hardware
modules such as the ADC, comparators, or the HLVD (high/low voltage detect)
can often be used for this purpose.
--------------------------------------------------------------------------------
This bootloader supports reprogramming of the microcontroller configuration bits,
however, it is strongly recommended never to do so, unless absolutely necessary.
Reprogramming the config bits is potentially risky, since it requires that the
new configuration bits be 100% compatible with USB operation (ex: oscillator
settings, etc.). If a .hex file with incorrect config bits is programmed
into this device, it can render the bootloader inoperable. Additionally,
unexpected power failure or device detachment during the reprogramming of the
config bits could result in unknown values getting stored in the config bits,
which could "brick" the application.
Normally, the application firmware project and this bootloader project should
be configured to use/set the exact same configuration bit values. Only one set
of configuration bits actually exists in the microcontroller, and these values
must be shared between the bootloader and application firmware.
*******************************************************************************/
/** I N C L U D E S **********************************************************/
#include "usb.h"
#include "HardwareProfile.h"
#include "BootPIC18NonJ.h"
/** V A R I A B L E S ********************************************************/
//NOTE: You must not use initalized variables in this bootloader project. This
//firmware project does not rely on the standard C initializer, which is
//responsible for setting up initialized variables in RAM. Therefore, all
//variables will be non-initialized/random at start up.
#ifndef __XC8__
#pragma udata
#endif
unsigned int uint_delay_counter;
//------------------------------------------------------------------------------
//Private prototypes
//------------------------------------------------------------------------------
void main(void);
void BootMain(void);
void LowVoltageCheck(void);
void InitializeSystem(void);
//Special "flash signature" located in the application program memory space (not
//part of the bootloader firmware program space). This flash signature is used
//to improve application recoverability/robustness, in the event the user unplugs
//the USB cable or AC power is lost during an erase/program/verify sequence.
#ifdef __XC8__
const unsigned int __at(APP_SIGNATURE_ADDRESS) FlashSignatureWord = APP_SIGNATURE_VALUE;
#else
#pragma romdata FLASH_SIG_SECTION = APP_SIGNATURE_ADDRESS
ROM unsigned int FlashSignatureWord = APP_SIGNATURE_VALUE;
#pragma code
#endif
//Special handling for modified linker script.
#ifndef __XC8__
//Note: This project uses a modified linker script, which does not include the
//c018i.o standard C initializer for C18 projects. Therefore, we must manually
//place a goto main() at the hardware reset vector.
#pragma code _entry_scn=0x000000 //Reset vector is at 0x00. Device begins executing code from 0x00 after a reset or POR event
void _entry (void)
{
_asm goto main _endasm
}
#pragma code
#endif
/******************************************************************************
* Function: void main(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This is the first code that executes during boot up of
* the microcontroller. This code checks to see if execution
* should stay in the "bootloader" mode, or if it should jump
* into the "application" (non-bootloder) execution mode.
* No other unrelated code should be added to this function.
*
* Note: THIS FUNCTION EXECUTES PRIOR TO INITIALIZATION OF THE C
* STACK. NO C INITIALIZATION OF STATIC VARIABLES OR RESOURCES
* WILL OCCUR, PRIOR TO EXECUTING THIS FUNCTION. THEREFORE,
* THE CODE IN THIS FUNCTION MUST NOT CALL OTHER FUNCTIONS OR
* PERFORM ANY OPERATIONS THAT WILL REQUIRE C INITIALIZED
* BEHAVIOR.
*****************************************************************************/
void main(void)
{
//Assuming the I/O pin check entry method is enabled, check the I/O pin value
//to see if we should stay in bootloader mode, or jump to normal applicaiton
//execution mode.
/*#ifdef ENABLE_IO_PIN_CHECK_BOOTLOADER_ENTRY
//Check Bootload Mode Entry Condition from the I/O pin (ex: place a
//pushbutton and pull up resistor on the pin)
if(0)
{
//Before going to application image however, make sure the image
//is properly signed and is intact.
goto DoFlashSignatureCheck;
}
else
{
//User is pressing the pushbutton. We should stay in bootloader mode
BootMain();
}
#endif //#ifdef ENABLE_IO_PIN_CHECK_BOOTLOADER_ENTRY
*/
DoFlashSignatureCheck:
//Check if the application region flash signature is valid
if(*(ROM unsigned int*)APP_SIGNATURE_ADDRESS == APP_SIGNATURE_VALUE)
{
//The flash signature was valid, implying the previous
//erase/program/verify operation was a success.
//Also make sure the first WORD of program memory in the app space
//is not blank, meaning there is an application image programmed into the device.
if(*(ROM unsigned int*)REMAPPED_APPLICATION_RESET_VECTOR != 0xFFFF)
{
//Go ahead and jump out of bootloader mode into the application run mode
#ifdef __XC8__
#asm
goto REMAPPED_APPLICATION_RESET_VECTOR
#endasm
#else //Must be C18 instead
_asm goto REMAPPED_APPLICATION_RESET_VECTOR _endasm
#endif
}
}
//else the application image is missing or corrupt. In this case, we
//need to stay in the bootloader mode, so the user has the ability to
//try (again) to re-program a valid application image into the device.
//We should stay in bootloader mode
BootMain();
}//end UninitializedMain
/******************************************************************************
* Function: void BootMain(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This is the main function for this bootloader mode firmware.
* if execution gets to this function, it is assumed that we
* want to stay in bootloader mode for now.
*
* Note: If adding code to this function, make sure to add it only
* after the C initializer like code at the top of this function.
* Additionally, code written in this project should not assume
* any variables or registers have been initialized by the C
* compiler (since they may not have been, if the user jumped
* from the application run mode into bootloader mode directly).
*****************************************************************************/
#ifdef __XC8__
void __at(0x30) BootMain(void)
#else
#pragma code BOOT_MAIN_SECTION=0x30
void BootMain(void)
#endif
{
//NOTE: The c018.o file is not included in the linker script for this project.
//The C initialization code in the c018.c (comes with C18 compiler in the src directory)
//file is instead modified and included here manually. This is done so as to provide
//a more convenient entry method into the bootloader firmware. Ordinarily the _entry_scn
//program code section starts at 0x00 and is created by the code of c018.o. However,
//the linker will not work if there is more than one section of code trying to occupy 0x00.
//Therefore, must not use the c018.o code, must instead manually include the useful code
//here instead.
//Make sure interrupts are disabled for this code (could still be on,
//if the application firmware jumped into the bootloader via software methods)
INTCON = 0x00;
//Initialize the C stack pointer, and other compiler managed items as
//normally done in the c018.c file (applicable when using C18 compiler)
#ifndef __XC8__
_asm
lfsr 1, _stack
lfsr 2, _stack
clrf TBLPTRU, 0
_endasm
#endif
//Clear the stack pointer, in case the user application jumped into
//bootloader mode with excessive junk on the call stack
STKPTR = 0x00;
// End of the important parts of the C initializer. This bootloader firmware does not use
// any C initialized user variables (idata memory sections). Therefore, the above is all
// the initialization that is required.
//Call other initialization code and (re)enable the USB module
InitializeSystem(); //Some USB, I/O pins, and other initialization
//Execute main loop
while(1)
{
ClrWdt();
//Need to call USBDeviceTasks() periodically. This function takes care of
//processing non-USB application related USB packets (ex: "Chapter 9"
//packets associated with USB enumeration)
USBDeviceTasks();
ProcessIO(); //This is where all the actual bootloader related data transfer/self programming takes
//place see ProcessIO() function in the BootPIC[xxxx].c file.
}//end while
}
#ifndef __XC8__
#pragma code
#endif
/******************************************************************************
* Function: static void InitializeSystem(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: InitializeSystem is a centralize initialization routine.
* All required USB initialization routines are called from
* here.
*
* User application initialization routine should also be
* called from here.
*
* Note: None
*****************************************************************************/
void InitializeSystem(void)
{
UserInit(); //Initialize bootloader application variables (see Bootxxxx.c file)
//Initialize USB module only after oscillator and other settings are compatible with USB operation
USBDeviceInit(); //Initializes USB module SFRs and firmware
//variables to known states.
}//end InitializeSystem
// ******************************************************************************************************
// ************** USB Callback Functions ****************************************************************
// ******************************************************************************************************
// The USB firmware stack will call the callback functions USBCBxxx() in response to certain USB related
// events. For example, if the host PC is powering down, it will stop sending out Start of Frame (SOF)
// packets to your device. In response to this, all USB devices are supposed to decrease their power
// consumption from the USB Vbus to <2.5mA* each. The USB module detects this condition (which according
// to the USB specifications is 3+ms of no bus activity/SOF packets) and then calls the USBCBSuspend()
// function. You should modify these callback functions to take appropriate actions for each of these
// conditions. For example, in the USBCBSuspend(), you may wish to add code that will decrease power
// consumption from Vbus to <2.5mA (such as by clock switching, turning off LEDs, putting the
// microcontroller to sleep, etc.). Then, in the USBCBWakeFromSuspend() function, you may then wish to
// add code that undoes the power saving things done in the USBCBSuspend() function.
//
// Note *: The "usb_20.pdf" specs indicate 500uA or 2.5mA, depending upon device classification. However,
// the USB-IF has officially issued an ECN (engineering change notice) changing this to 2.5mA for all
// devices. Make sure to re-download the latest specifications to get all of the newest ECNs.
/******************************************************************************
* Function: void USBCBWakeFromSuspend(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: The host may put USB peripheral devices in low power
* suspend mode (by "sending" 3+ms of idle). Once in suspend
* mode, the host may wake the device back up by sending non-
* idle state signalling.
*
* This call back is invoked when a wakeup from USB suspend
* is detected.
*
* Note: Before returning from this function, make sure that the
* oscillator settings are fully compatible with USB module
* operation.
* If using the two-speed startup feature, wakeup and execution
* will occur before the main oscillator + PLL has had a chance
* to start. Device will run from INTOSC (no PLL). However, the
* USB module cannot be clocked and receive correct USB data when
* it is not clocked with the correct frequency clock source.
* Therefore, when using two-speed startup, should execute software
* delay to prevent any other code from executing until the main
* oscillator is ready.
* The host will allow at least 10ms for USB "resume recovery", during
* which it will not try to communicate with the device.
*****************************************************************************/
void USBCBWakeFromSuspend(void)
{
//This code delays ~5ms @ 8MHz to execute (using C18 3.21 with full
//optimizations enabled), but takes much less time at 48MHz. This delay
//is to make sure the PLL is enabled and locked, in case two speed startup
//was enabled
DelayRoutine(0x300); //Device will switch clocks (if using two-speed startup) while executing this delay function
//Primary oscillator and PLL should be running by now.
//Do not return from this function until the oscillator is correctly configured and
//running in a USB compatible mode/frequency.
//Additional code for re-enabling I/O pins and increasing power draw from VBUS
//may be placed here (up to the maximum of 100mA [when unconfigured] or the
//amount specified in the configuration descriptor (when configured).
}
/******************************************************************************
* Function: void USBCBSuspend(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: Call back that is invoked when a USB suspend is detected
*
* Note: None
*****************************************************************************/
void USBCBSuspend(void)
{
Sleep(); // Go to sleep, wake up when a USB activity event occurs
//If using the WDT, should go back to sleep if awoke by WDT instead of USBIF
while((USBIF_FLAG == 0) && (RCONbits.TO == 0)) //If using the WDT, should go back to sleep if awoke by WDT instead of USBIF
{
Sleep(); //Entry into sleep clears WDT count, much like executing ClrWdt() instruction
}
//After the USB suspend event ends, you should re-configure your I/O pins
//for normal operation mode (which is allowed to consume more current).
//However, it is recommended to put this code in the USBCBWakeFromSuspend()
//function instead of here (so that this function will work with either
//sleeping or clock switching to a lower frequency).
}
/*******************************************************************
* Function: void USBCBInitEP(uint8_t ConfigurationIndex)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This function is called when the device becomes
* initialized, which occurs after the host sends a
* SET_CONFIGURATION request. This
* callback function should initialize the endpoints
* for the device's usage according to the current
* configuration.
*
* Note: If the host ever "unconfigures" the device, it will
* set the configuration to '0'. In this case, this
* callback gets called with ConfigurationIndex == 0, where
* the firmware should disable all non-EP0 endpoints (until
* the next non-zero SET_CONFIGURATION request is received,
* which will cause this callback to execute again).
*******************************************************************/
void USBCBInitEP(uint8_t ConfigurationIndex)
{
//Check what configuration "index" the host has requested us to select.
//Configuration index 0 is special and represents that the device should be
//un-configured. However, when the host sets the configuration (with index
//matching the valid/implemented configuration from the configuration descriptor),
//the firmware should enable the application endpoints associated with that
//configuration, and (re)initialize all application state variables associated
//with the USB application endpoints operation.
if(ConfigurationIndex == 1) //This application only implements one configuration, with index == 1.
{
//The host sent us a non-zero set configuration index. In this
//case we should prepare the application endpoints to be ready
//to use, and to (re-)initialize any application variables associated
//with the endpoints.
HIDInitEP();
//(Re-)Initialize the application variables associated with the USB interface
UserInit(); // See BootPIC[xxxx].c. Initializes the bootloader firmware state machine variables.
}
//else the host set the configuration back to 0 (indicating unconfigured), or
//to some higher (non-implemented value). In either case, we don't need to
//do anything specifically, unless the application requires some kind of
//"safe shutdown" code to execute after the host has deconfigured the device.
}
/*******************************************************************
* Function: void USBCBCheckOtherReq(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: This function is called when the USB stack receives a
* new control transfer SETUP packet from the host. The
* USB stack handles normal USB "Chapter 9" requests internally,
* but some control transfer requests are class specific. In
* order to handle these class specific requests, you must call
* the class handler's firmware control transfer handler function.
* If implementing a composite device with multiple classes
* implemented, call each of the handlers in the below callback.
*
* Note: None
*******************************************************************/
void USBCBCheckOtherReq(void)
{
USBCheckHIDRequest();
}
//Compiler mode and version check. This code needs to fit within the [0x000-0xFFF] program
//memory region that is reserved for use by the bootloader. However, if this
//code is built in XC8 Standard or Free mode (instead of PRO),
//the code may be too large to fit within the region, and a variety of linker
//error messages (ex: "can't find space") will result. Unfortunately these
//linker error messages can be cryptic to a user, so instead we add a deliberate
//#error to make a more human friendly error appear, in the event the wrong
//compiler mode is attempted to use to build this code. If you get this error
//message, please upgrade to the PRO compiler, and then use the mode
//(ex: build configuration --> XC8 compiler --> Option Categories: Optimizations --> Operation Mode: PRO)
//#ifdef __XC8__
// #if _HTC_EDITION_ < 2 //Check if PRO, Standard, or Free mode
// #error "This bootloader project must be built in PRO mode to fit within the reserved region. Double click this message for more details."
// #endif
//#endif
/** EOF main.c ***************************************************************/