Microchip Harmony and Bootloaders

I’ve been messing around with a Digilent Max32, and have been using Microchip’s MPLAB Harmony framework to program the device.  The framework is rather nice, but it can be a little confusing at times.  It took me a while to figure out how to work the bootloader on it, so this post will go into how to setup your projects.

Prerequisites:

  1. PIC32 based device.  In our case, this will be the Max32, but any PIC based device should work fine.
  2. MPLAB X, Microchip’s dev platform based on Netbeans.
  3. Microchip XC32 compiler.  This should be a part of your MPLAB X install, but if not you can download it from Microchip’s website.  I am using version 1.42.
  4. PIC Programmer.  I have an ICD3, but it should be possible to use a PICKit programmer as well.

Part I: Install Harmony

Before we can mess around with Harmony, we need to install the latest version.  In our case, we will be using the current beta version of 2.0.

  1. Download the new Harmony Configurator from Microchip’s website.
  2. Go to the ‘Downloads’ tab, and download the proper version for your system

    Step 1 and 2 – Download Harmony and Configurator

  3. Run the installer for Harmony.  Make sure that you remember where you installed it, you will need this information later.
  4. In MPLAB X, go to Tools->Plugins.

    Plugins location

  5. Go to the ‘Downloaded’ tab
  6. Click ‘Add Plugins’
  7. Browse to where you downloaded the Harmony plug-in to, and select it.

    How to install the downloaded plugin

  8. The plugin will now show up in your Plugins window.  Click the ‘Install’ button.
  9. The plugin will install.  Once it does, you will have to restart MPLAB X.

Part II: Create Bootloader project

Note that before you come here, you must have MPLAB X Harmony and the proper Harmony plugin installed.

  1. Create a new project by going to File->New Project.
  2. Select ’32-bit MPLAB Harmony Project’ and click ‘Next’

    Basic project configuration

  3. Fill in all of the information on this screen.  Don’t forget to set the PIC device that you are using.
  4. If the Harmony configurator does not open up for you automatically, go to Tools -> Embedded -> MPLAB Harmony Configurator

    Open up the Harmony Configurator if it closes

  5. Under the ‘Application Configuration’ section, we want to create an application(we will call it max32_bl) and click ‘Generate application code for selected Harmony components’.  We want to generate code for the bootloader in this area.

    Bootloader configuration #1

  6. Under the ‘Harmony Framework Configuration’ section, select ‘Bootloader Library’.  We will be using the USART bootloader.  Under the ‘Bootloader or Applicaton?’ section, select ‘Bootloader’.  Also select ‘Ports’ under ‘System Services’

    Bootloader configuration #2

  7. Click the ‘Generate Code’ button on the Configurator screen.  We will have a number of new C/H files for us to edit.  Also, there is now a custom linker script for us under the ‘Linker Files’ folder.
  8. Now that code has been generated, there are a few things that we want to do to make it clear what is happening.  First, we don’t need the legacy bootloader options.  So under ‘Header Files’, go to app/system_config/default/system_config.h, and comment out the preprocessor macros BOOTLOADER_LEGACY and BTL_SWITCH

    Delete/comment out these macros

  9. The first thing for us to edit in the code is to open up our max32_bl.c file.  This file contains generated code for us to modify.  The first thing is that we need to move the line that says
    BOOTLOADER_ForceBootloadRegister(MAX32_BL_Bootloader_ForceEvent);

    to be in the

    MAX32_BL_Initialize

    function.  Otherwise, the bootloader code will not call our ForceEvent function.

  10. Now, let’s add a splash screen.  For now, we can simply add a simple function to print out to the UART, and then call that function once in the MAX32_BL_Tasks function, in the MAX32_BL_STATE_INIT case statement of our switch.  Here’s the simple UART printing function:
    static void writeStringToUART( const char* string ){
     int pos = 0;
     
     while( string[ pos ] != 0 ){
     while( DRV_USART0_TransmitBufferIsFull() ){}
     DRV_USART0_WriteByte( string[ pos++ ] );
     }
    }
  11. You should now be able to program your device with the bootloader.  Open up a terminal with a terminal emulator such as Putty, and if you have done everything correctly you should see a splash screen whenever you reset the processor.  On the MAX32, this is accomplished by pressing the reset switch.
  12. Let’s also make sure that we can get to the bootloader manually.  To do this, we will have to configure one pin on the chip as a GPIO input, with a pull-up resistor.  Go back to the Harmony Configurator screen, and go to the ‘Pin Settings’ tab.

    Pin settings

  13. Choose a pin to use as an input.  In my case, I chose RG6.  This corresponds to pin 52 on the Max32 board.  I will also enable the pull up resistor on this pin, so that when the pin is shorted to ground the bootloader will start.
  14. Go back to our max32_bl file, and modify MAX32_BL_Bootloader_ForceEvent to look like the following(change your pin if you used a different one).
    int MAX32_BL_Bootloader_ForceEvent(void)
    {
     int rg6 = PLIB_PORTS_PinGet (PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_6);
     
     if( !rg6 ){
     writeStringToUART( "Button held, forcing bootloader\n" );
     return 1;
     }
     
     /* Check the trigger memory location and return true/false. */
     if (*(uint32_t *)MAX32_BL_BOOTLOADER_TRIGGER_MEMORY_ADDRESS == 0xFFFFFFFF)
     return (1);
     else
     return (0);
    }
  15. Build and re-flash your device.  When you short pin RG6 to GND, the string “Button held, forcing bootloader” should now print out on your console when the bootloader starts.

Here’s the entire file for reference:

/*******************************************************************************
  MPLAB Harmony Application Source File
  
  Company:
    Microchip Technology Inc.
  
  File Name:
    max32_bl.c

  Summary:
    This file contains the source code for the MPLAB Harmony application.

  Description:
    This file contains the source code for the MPLAB Harmony application.  It 
    implements the logic of the application's state machine and it may call 
    API routines of other MPLAB Harmony modules in the system, such as drivers,
    system services, and middleware.  However, it does not call any of the
    system interfaces (such as the "Initialize" and "Tasks" functions) of any of
    the modules in the system or make any assumptions about when those functions
    are called.  That is the responsibility of the configuration-specific system
    files.
 *******************************************************************************/

// DOM-IGNORE-BEGIN
/*******************************************************************************
Copyright (c) 2013-2014 released Microchip Technology Inc.  All rights reserved.

Microchip licenses to you the right to use, modify, copy and distribute
Software only when embedded on a Microchip microcontroller or digital signal
controller that is integrated into your product or third party product
(pursuant to the sublicense terms in the accompanying license agreement).

You should refer to the license agreement accompanying this Software for
additional information regarding your rights and obligations.

SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER
CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR
OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
 *******************************************************************************/
// DOM-IGNORE-END


// *****************************************************************************
// *****************************************************************************
// Section: Included Files 
// *****************************************************************************
// *****************************************************************************

#include "max32_bl.h"

#include <stdio.h>

// *****************************************************************************
// *****************************************************************************
// Section: Global Data Definitions
// *****************************************************************************
// *****************************************************************************

// *****************************************************************************
/* Application Data

  Summary:
    Holds application data

  Description:
    This structure holds the application's data.

  Remarks:
    This structure should be initialized by the APP_Initialize function.
    
    Application strings and buffers are be defined outside this structure.
*/

MAX32_BL_DATA max32_blData;
#define MAX32_BL_BOOTLOADER_TRIGGER_MEMORY_ADDRESS					0x9D000000		

static void writeStringToUART( const char* string ){
    int pos = 0;
    
    while( string[ pos ] != 0 ){
        while( DRV_USART0_TransmitBufferIsFull() ){}
        DRV_USART0_WriteByte( string[ pos++ ] );
    }
}

static void writeIntAsCharToUART( int i ){
    while( DRV_USART0_TransmitBufferIsFull() ){}
    DRV_USART0_WriteByte( i + 48 );
}

static void waitForUARTFlush(){
    while( !PLIB_USART_TransmitterIsEmpty( USART_ID_1 ) ){}
}

// *****************************************************************************
// *****************************************************************************
// Section: Application Callback Functions
// *****************************************************************************
// *****************************************************************************
/******************************************************************************
  Function:
    static void MAX32_BL_Bootloader_ForceEvent (void)
    
   Remarks:
    Sets a trigger to be passed to force bootloader callback.
	Run bootloader if memory location == '0xFFFFFFFF' otherwise jump to user 
	application.
*/ 
int MAX32_BL_Bootloader_ForceEvent(void)
{
    int rg6 = PLIB_PORTS_PinGet (PORTS_ID_0, PORT_CHANNEL_G, PORTS_BIT_POS_6);
    
    if( !rg6 ){
        writeStringToUART( "Button held, forcing bootloader\n" );
        return 1;
    }
    
    /* Check the trigger memory location and return true/false. */
    if (*(uint32_t *)MAX32_BL_BOOTLOADER_TRIGGER_MEMORY_ADDRESS == 0xFFFFFFFF)
        return (1);
    else
		return (0);
}

/* TODO:  Add any necessary callback functions.
*/

// *****************************************************************************
// *****************************************************************************
// Section: Application Local Functions
// *****************************************************************************
// *****************************************************************************


/* TODO:  Add any necessary local functions.
*/


// *****************************************************************************
// *****************************************************************************
// Section: Application Initialization and State Machine Functions
// *****************************************************************************
// *****************************************************************************

/*******************************************************************************
  Function:
    void MAX32_BL_Initialize ( void )

  Remarks:
    See prototype in max32_bl.h.
 */

void MAX32_BL_Initialize ( void )
{
    /* Place the App state machine in its initial state. */
    max32_blData.state = MAX32_BL_STATE_INIT;

    
    /* TODO: Initialize your application's state machine and other
     * parameters.
     */
    BOOTLOADER_ForceBootloadRegister(MAX32_BL_Bootloader_ForceEvent);
}

static void printSplash(){    
    writeStringToUART( "MAX32 Bootloader\n" );
    writeStringToUART( "Version " );
    writeIntAsCharToUART( MAJOR_VERSION );
    writeStringToUART( "." );
    writeIntAsCharToUART( MINOR_VERSION );
    writeStringToUART( "\n" );
    writeStringToUART( __DATE__ );
    writeStringToUART( " " );
    writeStringToUART( __TIME__ );
    writeStringToUART( "\n" );
    
    waitForUARTFlush();
}

/******************************************************************************
  Function:
    void MAX32_BL_Tasks ( void )

  Remarks:
    See prototype in max32_bl.h.
 */

void MAX32_BL_Tasks ( void )
{

    /* Check the application's current state. */
    switch ( max32_blData.state )
    {
        /* Application's initial state. */
        case MAX32_BL_STATE_INIT:
        {
            bool appInitialized = true;
       
        
            if (appInitialized)
            {
            
                max32_blData.state = MAX32_BL_STATE_SERVICE_TASKS;
                
                //print splash screen.  this will happen only once
                printSplash();
            }
            break;
        }

        case MAX32_BL_STATE_SERVICE_TASKS:
        {
        
            break;
        }

        /* TODO: implement your application state machine.*/
        

        /* The default state should never be executed. */
        default:
        {
            /* TODO: Handle error in application's state machine. */
            break;
        }
    }
}

 

/*******************************************************************************
 End of File
 */

Part III: Create our main program

Now that we have our bootloader setup, we need to create the main program that will get loaded by the bootloader.

  1. Create a new project by going to File->New Project.
  2. Select ’32-bit MPLAB Harmony Project’ and click ‘Next’
  3. Fill in all of the information on this screen.  Don’t forget to set the PIC device that you are using.
  4. If the Harmony configurator does not open up for you automatically, go to Tools -> Embedded -> MPLAB Harmony Configurator
  5. Under the ‘Application Configuration’ section, we want to create an application(we will call it max32_program) and click ‘Generate application code for selected Harmony components’.  We want code for the Timer and USART to give us some code to work with.

    Main program configuration #1

  6. Under the ‘Harmony Framework Configuration’ section, select ‘Bootloader Library’.  We will be using the USART bootloader.  Under the ‘Bootloader or Applicaton?’ section, select ‘Application’

    Main program configuration #2

  7. Under the ‘Harmony Framework Configuration’ section, also make sure that you have drivers for the timer and USART 0 selected.  The USART should be running at the same baud rate as the bootloader.
  8. Go to the ‘Pin Settings’ page as we did before.  For the MAX32, pin RC1 is an output that drives an LED.  We will use RG6 as an input as we did before on the botloader.  Setup those settings.

    Pin diagram for the main program

  9. Click ‘Generate code’
  10. Your project is now created with some basic information.  Since we set up our timer to use interrupts, we now have a TimerCallback function in mainapp.c.  Let’s modify it to toggle the RC1 LED every time the timer is called.  The code will now look like this:
    static void TimerCallback ( uintptr_t context, uint32_t alarmCount )
    {
     static int state = 0;
     PLIB_PORTS_PinWrite (PORTS_ID_0, PORT_CHANNEL_C, PORTS_BIT_POS_1, state);
     state = !state;
    }
  11. Build your project.  The hex file that is generated is what you want to send to the bootloader.  Note that this hex file can’t be sent as-is, you must convert it to binary and use Microchip’s protocol.  See the next section for some discussion.

Part IV: Bootloading

Now that we have our bootloader and a program, we should try to actually load information onto the device using the bootloader.  To do this, we will need a program to write the information.  You can use either Microchip’s version in AN1388, or I wrote a version that is cross-platform compatible using my CSerial library.

Whichever method you use, once you have built the actual program you need to send the hex file across to get the bootloader to write the data.  If everything has gone correctly, you will then be able to re-set the board and have your application running.

One more tip: If you can program the entire chip at once(e.g. you have an ICD3), if you set the bootloader project as a loadable of your main project you can flash both projects to the chip at the same time.  Since they get flashed in different locations, there’s no conflict.  Note that this won’t work with the lower-end programmers.

 

This tutorial probably skims over a few steps, but it should at least be enough to get people started and working with Harmony and its bootloader.  Questions? Add a comment and I will see about expanding upon either this post or a later one.

9 thoughts on “Microchip Harmony and Bootloaders

  1. Pingback: Harmony LiveUpdate | Juan Gago

  2. Great tutorial!. I spend like 4 days fighting to understand the bootloader procedure in Harmony. I was trying to put the bootloader and the application on the same project!. Then I read this tutorial and everything make sense now!.

    There is a warning during the compilation of the application project regarding incorrect configurations registers. This is ok given that there is no information on the boot flash section, where those configuration registers resides, after choosing the “Build and Application Linker Script?”.

    Following the tutorial I was able to implement the basic Harmony bootloader on the microcontroller PIC32MZ1024EFE064.

  3. Thank you for this tutorial.
    I got the boot loader working and was able to program the hex file the application worked as expected but there is one issue that I am facing once I program the hex file I am not able to connect using AN1388 it seems like the bootloader got corrupted or something. This happens only after programming a hex file via bootloader using AN1388.
    Can you please suggest me what might have went wrong.

    • The bootloader will only run if there is nothing on the PIC, or if you initialize the bootloader somehow(for example, by holding down a button that forces the bootloader to start). Since your application is running after you program it, the AN1388 program won’t run properly.

      • Yes, thank you for replying,
        If I reset the device (turning power OFF/ON) I get the printsplash screen on the serial port application does this mean bootloader is being invoked after reset?? But still the connection fails as if bootloader directly jumps to application,Is there any way I can force start the bootloader without any botton (using interrupt of uart or something), since I am working on a custom board I dont have a provision of adding any button, all pins are being used right now.

        • U can add a uart on the bootloader project to listen for a single char or some sequence of characters at the begging for lets say 3 seconds. That could be your trigger to enter the bootloader.

          If u enable Rx interrupts u can set a flag on the uartRx ISR. If this flag is declared as a global variable, u can use it as a trigger to enter the bootloader. U will need to wait for the interrupt for a given time, after which you should skip bootloader and load the application instead. If posible try not to use the same uart used by the bootloader to transfer data. Otherwise, take care to don’t send to many characters, specially if there is a SOF (0x01) on it.

          • The other thing that you can do is to have a command that will wipe the memory that the bootloader is looking for. Since the bootloader checks a certain memory address on the flash, simply clear that memory address to force the bootloader to run the next time it boots. Pair this with a command that re-sets the PIC and you should be good.

          • Thank you,
            It worked, I erased the memory address bootloder was looking for.

  4. Hello again,
    I need some help with the linker files see if you can help?
    I am able to run the application via the bootlader with interrupts disabled but when I enable the interrupts the application gets stuck (I guess jumps to some unknown memory location).
    can you suggest any solutions?

    here is my application linker
    /* Default linker script, for normal executables */
    OUTPUT_FORMAT(“elf32-tradlittlemips”)
    OUTPUT_ARCH(pic32mx)
    ENTRY(_reset)
    /*
    * Provide for a minimum stack and heap size
    * – _min_stack_size – represents the minimum space that must be made
    * available for the stack. Can be overridden from
    * the command line using the linker’s –defsym option.
    * – _min_heap_size – represents the minimum space that must be made
    * available for the heap. Can be overridden from
    * the command line using the linker’s –defsym option.
    */
    EXTERN (_min_stack_size _min_heap_size)

    /*************************************************************************
    * Processor-specific object file. Contains SFR definitions.
    *************************************************************************/
    INPUT(“processor.o”)

    /*************************************************************************
    * Processor-specific peripheral libraries are optional
    *************************************************************************/
    OPTIONAL(“libmchp_peripheral.a”)

    /*************************************************************************
    * For interrupt vector handling
    *************************************************************************/
    PROVIDE(_vector_spacing = 0x00000001);
    _ebase_address = 0x9D001000;

    /*************************************************************************
    * Memory Address Equates
    * _RESET_ADDR — Reset Vector
    * _BEV_EXCPT_ADDR — Boot exception Vector
    * _DBG_EXCPT_ADDR — In-circuit Debugging Exception Vector
    * _DBG_CODE_ADDR — In-circuit Debug Executive address
    * _DBG_CODE_SIZE — In-circuit Debug Executive size
    * _GEN_EXCPT_ADDR — General Exception Vector
    *************************************************************************/
    _ORIGINAL_RESET_ADDR = 0xBFC00000;
    _RESET_ADDR = (0x9D000000);
    _GEN_EXCPT_ADDR = _ebase_address + 0x180;

    /*************************************************************************
    * Memory Regions
    *
    * Memory regions without attributes cannot be used for orphaned sections.
    * Only sections specifically assigned to these regions can be allocated
    * into these regions.
    *************************************************************************/
    MEMORY
    {
    startup_without_bootloader : ORIGIN = 0xBFC00000, LENGTH = 0x8
    kseg0_program_mem (rx) : ORIGIN = (0x9D000000), LENGTH = 0x100000 /* All C Files will be located here */
    kseg0_boot_mem : ORIGIN = 0x9D000000, LENGTH = 0x0 /* This memory region is dummy */
    config3 : ORIGIN = 0xBFC02FF0, LENGTH = 0x4
    config2 : ORIGIN = 0xBFC02FF4, LENGTH = 0x4
    config1 : ORIGIN = 0xBFC02FF8, LENGTH = 0x4
    config0 : ORIGIN = 0xBFC02FFC, LENGTH = 0x4
    kseg1_data_mem (w!x) : ORIGIN = 0xA0000000, LENGTH = 0x40000
    sfrs : ORIGIN = 0xBF800000, LENGTH = 0x100000
    debug_exec_mem : ORIGIN = 0xBFC02000, LENGTH = 0xFF0
    configsfrs : ORIGIN = 0xBFC02FF0, LENGTH = 0x10
    }

    /*************************************************************************
    * Configuration-word sections
    *************************************************************************/
    SECTIONS
    {
    .config_BFC02FF0 : {
    KEEP(*(.config_BFC02FF0))
    } > config3
    .config_BFC02FF4 : {
    KEEP(*(.config_BFC02FF4))
    } > config2
    .config_BFC02FF8 : {
    KEEP(*(.config_BFC02FF8))
    } > config1
    .config_BFC02FFC : {
    KEEP(*(.config_BFC02FFC))
    } > config0
    }
    SECTIONS
    {
    .startup_without_bootloader _ORIGINAL_RESET_ADDR :
    {
    /* creates a ?j _reset? instruction at address _ORIGINAL_RESET_ADDR
    _reset must be within the same 256MB boundary as the original reset
    vector in order for this to work do to the limitations of the ?j? instruction.
    */
    LONG( ((_reset >> 2) & 0x03FFFFFF) | 0x08000000);
    LONG(0);
    } > startup_without_bootloader
    /* Boot Sections */
    .reset _RESET_ADDR :
    {
    KEEP(*(.reset))
    KEEP(*(.reset.startup))
    } > kseg0_program_mem
    .app_excpt _GEN_EXCPT_ADDR :
    {
    KEEP(*(.gen_handler))
    } > kseg0_program_mem

    .vector_0 _ebase_address + 0x200 :
    {
    KEEP(*(.vector_0))
    } > kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_0) <= (_vector_spacing << 5), "function at exception vector 0 too large")
    .vector_1 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_1) <= (_vector_spacing << 5), "function at exception vector 1 too large")
    .vector_2 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_2) <= (_vector_spacing << 5), "function at exception vector 2 too large")
    .vector_3 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_3) <= (_vector_spacing << 5), "function at exception vector 3 too large")
    .vector_4 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_4) <= (_vector_spacing << 5), "function at exception vector 4 too large")
    .vector_5 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_5) <= (_vector_spacing << 5), "function at exception vector 5 too large")
    .vector_6 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_6) <= (_vector_spacing << 5), "function at exception vector 6 too large")
    .vector_7 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_7) <= (_vector_spacing << 5), "function at exception vector 7 too large")
    .vector_8 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_8) <= (_vector_spacing << 5), "function at exception vector 8 too large")
    .vector_9 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_9) <= (_vector_spacing << 5), "function at exception vector 9 too large")
    .vector_10 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_10) <= (_vector_spacing << 5), "function at exception vector 10 too large")
    .vector_11 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_11) <= (_vector_spacing << 5), "function at exception vector 11 too large")
    .vector_12 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_12) <= (_vector_spacing << 5), "function at exception vector 12 too large")
    .vector_13 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_13) <= (_vector_spacing << 5), "function at exception vector 13 too large")
    .vector_14 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_14) <= (_vector_spacing << 5), "function at exception vector 14 too large")
    .vector_15 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_15) <= (_vector_spacing << 5), "function at exception vector 15 too large")
    .vector_16 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_16) <= (_vector_spacing << 5), "function at exception vector 16 too large")
    .vector_17 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_17) <= (_vector_spacing << 5), "function at exception vector 17 too large")
    .vector_18 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_18) <= (_vector_spacing << 5), "function at exception vector 18 too large")
    .vector_19 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_19) <= (_vector_spacing << 5), "function at exception vector 19 too large")
    .vector_20 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_20) <= (_vector_spacing << 5), "function at exception vector 20 too large")
    .vector_21 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_21) <= (_vector_spacing << 5), "function at exception vector 21 too large")
    .vector_22 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_22) <= (_vector_spacing << 5), "function at exception vector 22 too large")
    .vector_23 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_23) <= (_vector_spacing << 5), "function at exception vector 23 too large")
    .vector_24 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_24) <= (_vector_spacing << 5), "function at exception vector 24 too large")
    .vector_25 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_25) <= (_vector_spacing << 5), "function at exception vector 25 too large")
    .vector_26 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_26) <= (_vector_spacing << 5), "function at exception vector 26 too large")
    .vector_27 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_27) <= (_vector_spacing << 5), "function at exception vector 27 too large")
    .vector_28 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_28) <= (_vector_spacing << 5), "function at exception vector 28 too large")
    .vector_29 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_29) <= (_vector_spacing << 5), "function at exception vector 29 too large")
    .vector_30 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_30) <= (_vector_spacing << 5), "function at exception vector 30 too large")
    .vector_31 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_31) <= (_vector_spacing << 5), "function at exception vector 31 too large")
    .vector_32 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_32) <= (_vector_spacing << 5), "function at exception vector 32 too large")
    .vector_33 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_33) <= (_vector_spacing << 5), "function at exception vector 33 too large")
    .vector_34 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_34) <= (_vector_spacing << 5), "function at exception vector 34 too large")
    .vector_35 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_35) <= (_vector_spacing << 5), "function at exception vector 35 too large")
    .vector_36 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_36) <= (_vector_spacing << 5), "function at exception vector 36 too large")
    .vector_37 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_37) <= (_vector_spacing << 5), "function at exception vector 37 too large")
    .vector_38 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_38) <= (_vector_spacing << 5), "function at exception vector 38 too large")
    .vector_39 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_39) <= (_vector_spacing << 5), "function at exception vector 39 too large")
    .vector_40 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_40) <= (_vector_spacing << 5), "function at exception vector 40 too large")
    .vector_41 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_41) <= (_vector_spacing << 5), "function at exception vector 41 too large")
    .vector_42 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_42) <= (_vector_spacing << 5), "function at exception vector 42 too large")
    .vector_43 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_43) <= (_vector_spacing << 5), "function at exception vector 43 too large")
    .vector_44 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_44) <= (_vector_spacing << 5), "function at exception vector 44 too large")
    .vector_45 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_45) <= (_vector_spacing << 5), "function at exception vector 45 too large")
    .vector_46 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_46) <= (_vector_spacing << 5), "function at exception vector 46 too large")
    .vector_47 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_47) <= (_vector_spacing << 5), "function at exception vector 47 too large")
    .vector_48 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_48) <= (_vector_spacing << 5), "function at exception vector 48 too large")
    .vector_49 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_49) <= (_vector_spacing << 5), "function at exception vector 49 too large")
    .vector_50 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_50) <= (_vector_spacing << 5), "function at exception vector 50 too large")
    .vector_51 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_51) <= (_vector_spacing << 5), "function at exception vector 51 too large")
    .vector_52 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_52) <= (_vector_spacing << 5), "function at exception vector 52 too large")
    .vector_53 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_53) <= (_vector_spacing << 5), "function at exception vector 53 too large")
    .vector_54 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_54) <= (_vector_spacing << 5), "function at exception vector 54 too large")
    .vector_55 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_55) <= (_vector_spacing << 5), "function at exception vector 55 too large")
    .vector_56 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_56) <= (_vector_spacing << 5), "function at exception vector 56 too large")
    .vector_57 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_57) <= (_vector_spacing << 5), "function at exception vector 57 too large")
    .vector_58 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_58) <= (_vector_spacing << 5), "function at exception vector 58 too large")
    .vector_59 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_59) <= (_vector_spacing << 5), "function at exception vector 59 too large")
    .vector_60 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_60) <= (_vector_spacing << 5), "function at exception vector 60 too large")
    .vector_61 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_61) <= (_vector_spacing << 5), "function at exception vector 61 too large")
    .vector_62 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_62) <= (_vector_spacing << 5), "function at exception vector 62 too large")
    .vector_63 _ebase_address + 0x200 + (_vector_spacing < kseg0_program_mem
    ASSERT (_vector_spacing == 0 || SIZEOF(.vector_63) <= (_vector_spacing < kseg0_boot_mem
    /* Code Sections – Note that input sections *(.text) and *(.text.*)
    ** are not mapped here. Starting in C32 v2.00, the best-fit allocator
    ** locates them, so that .text may flow around absolute sections
    ** as needed.
    */
    .text :
    {
    *(.stub .gnu.linkonce.t.*)
    KEEP (*(.text.*personality*))
    *(.mips16.fn.*)
    *(.mips16.call.*)
    *(.gnu.warning)
    . = ALIGN(4) ;
    } >kseg0_program_mem
    /* Global-namespace object initialization */
    .init :
    {
    KEEP (*crti.o(.init))
    KEEP (*crtbegin.o(.init))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o *crtn.o ).init))
    KEEP (*crtend.o(.init))
    KEEP (*crtn.o(.init))
    . = ALIGN(4) ;
    } >kseg0_program_mem
    .fini :
    {
    KEEP (*(.fini))
    . = ALIGN(4) ;
    } >kseg0_program_mem
    .preinit_array :
    {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4) ;
    } >kseg0_program_mem
    .init_array :
    {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4) ;
    } >kseg0_program_mem
    .fini_array :
    {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4) ;
    } >kseg0_program_mem
    .ctors :
    {
    /* XC32 uses crtbegin.o to find the start of
    the constructors, so we make sure it is
    first. Because this is a wildcard, it
    doesn’t matter if the user does not
    actually link against crtbegin.o; the
    linker won’t look for a file to match a
    wildcard. The wildcard also means that it
    doesn’t matter which directory crtbegin.o
    is in. */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don’t want to include the .ctor section from
    the crtend.o file until after the sorted ctors.
    The .ctor section from the crtend file contains the
    end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
    . = ALIGN(4) ;
    } >kseg0_program_mem
    .dtors :
    {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
    . = ALIGN(4) ;
    } >kseg0_program_mem
    /* Read-only sections */
    .rodata :
    {
    *( .gnu.linkonce.r.*)
    *(.rodata1)
    . = ALIGN(4) ;
    } >kseg0_program_mem
    /*
    * Small initialized constant global and static data can be placed in the
    * .sdata2 section. This is different from .sdata, which contains small
    * initialized non-constant global and static data.
    */
    .sdata2 ALIGN(4) :
    {
    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
    . = ALIGN(4) ;
    } >kseg0_program_mem
    /*
    * Uninitialized constant global and static data (i.e., variables which will
    * always be zero). Again, this is different from .sbss, which contains
    * small non-initialized, non-constant global and static data.
    */
    .sbss2 ALIGN(4) :
    {
    *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
    . = ALIGN(4) ;
    } >kseg0_program_mem
    .eh_frame_hdr :
    {
    *(.eh_frame_hdr)
    } >kseg0_program_mem
    . = ALIGN(4) ;
    .eh_frame : ONLY_IF_RO
    {
    KEEP (*(.eh_frame))
    } >kseg0_program_mem
    . = ALIGN(4) ;
    .gcc_except_table : ONLY_IF_RO
    {
    *(.gcc_except_table .gcc_except_table.*)
    } >kseg0_program_mem
    . = ALIGN(4) ;
    .dbg_data (NOLOAD) :
    {
    . += (DEFINED (_DEBUGGER) ? 0x200 : 0x0);
    } >kseg1_data_mem
    .jcr :
    {
    KEEP (*(.jcr))
    . = ALIGN(4) ;
    } >kseg1_data_mem
    .eh_frame : ONLY_IF_RW
    {
    KEEP (*(.eh_frame))
    } >kseg1_data_mem
    . = ALIGN(4) ;
    .gcc_except_table : ONLY_IF_RW
    {
    *(.gcc_except_table .gcc_except_table.*)
    } >kseg1_data_mem
    . = ALIGN(4) ;
    /* Persistent data – Use the new C ‘persistent’ attribute instead. */
    .persist :
    {
    _persist_begin = .;
    *(.persist .persist.*)
    *(.pbss .pbss.*)
    . = ALIGN(4) ;
    _persist_end = .;
    } >kseg1_data_mem
    /*
    * Note that input sections named .data* are no longer mapped here.
    * Starting in C32 v2.00, the best-fit allocator locates them, so
    * that they may flow around absolute sections as needed.
    */
    .data :
    {
    *( .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
    *(.data1)
    . = ALIGN(4) ;
    } >kseg1_data_mem
    . = .;
    _gp = ALIGN(16) + 0x7ff0;
    .got ALIGN(4) :
    {
    *(.got.plt) *(.got)
    . = ALIGN(4) ;
    } >kseg1_data_mem /* AT>kseg0_program_mem */
    /*
    * Note that “small” data sections are still mapped in the linker
    * script. This ensures that they are grouped together for
    * gp-relative addressing. Absolute sections are allocated after
    * the “small” data sections so small data cannot flow around them.
    */
    /*
    * We want the small data sections together, so single-instruction offsets
    * can access them all, and initialized data all before uninitialized, so
    * we can shorten the on-disk segment size.
    */
    .sdata ALIGN(4) :
    {
    _sdata_begin = . ;
    *(.sdata .sdata.* .gnu.linkonce.s.*)
    . = ALIGN(4) ;
    _sdata_end = . ;
    } >kseg1_data_mem
    .lit8 :
    {
    *(.lit8)
    } >kseg1_data_mem
    .lit4 :
    {
    *(.lit4)
    } >kseg1_data_mem
    . = ALIGN (4) ;
    _data_end = . ;
    _bss_begin = . ;
    .sbss ALIGN(4) :
    {
    _sbss_begin = . ;
    *(.dynsbss)
    *(.sbss .sbss.* .gnu.linkonce.sb.*)
    *(.scommon)
    _sbss_end = . ;
    . = ALIGN(4) ;
    } >kseg1_data_mem
    /*
    * Align here to ensure that the .bss section occupies space up to
    * _end. Align after .bss to ensure correct alignment even if the
    * .bss section disappears because there are no input sections.
    *
    * Note that input sections named .bss* are no longer mapped here.
    * Starting in C32 v2.00, the best-fit allocator locates them, so
    * that they may flow around absolute sections as needed.
    *
    */
    .bss :
    {
    *(.dynbss)
    *(COMMON)
    /* Align here to ensure that the .bss section occupies space up to
    _end. Align after .bss to ensure correct alignment even if the
    .bss section disappears because there are no input sections. */
    . = ALIGN(. != 0 ? 4 : 1);
    } >kseg1_data_mem
    . = ALIGN(4) ;
    _end = . ;
    _bss_end = . ;
    /* Starting with C32 v2.00, the heap and stack are dynamically
    * allocated by the linker.
    */
    /*
    * RAM functions go at the end of our stack and heap allocation.
    * Alignment of 2K required by the boundary register (BMXDKPBA).
    *
    * RAM functions are now allocated by the linker. The linker generates
    * _ramfunc_begin and _bmxdkpba_address symbols depending on the
    * location of RAM functions.
    */
    _bmxdudba_address = LENGTH(kseg1_data_mem) ;
    _bmxdupba_address = LENGTH(kseg1_data_mem) ;
    /* The .pdr section belongs in the absolute section */
    /DISCARD/ : { *(.pdr) }
    .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
    .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
    .mdebug.abi32 : { KEEP(*(.mdebug.abi32)) }
    .mdebug.abiN32 : { KEEP(*(.mdebug.abiN32)) }
    .mdebug.abi64 : { KEEP(*(.mdebug.abi64)) }
    .mdebug.abiO64 : { KEEP(*(.mdebug.abiO64)) }
    .mdebug.eabi32 : { KEEP(*(.mdebug.eabi32)) }
    .mdebug.eabi64 : { KEEP(*(.mdebug.eabi64)) }
    .gcc_compiled_long32 : { KEEP(*(.gcc_compiled_long32)) }
    .gcc_compiled_long64 : { KEEP(*(.gcc_compiled_long64)) }
    /* Stabs debugging sections. */
    .stab 0 : { *(.stab) }
    .stabstr 0 : { *(.stabstr) }
    .stab.excl 0 : { *(.stab.excl) }
    .stab.exclstr 0 : { *(.stab.exclstr) }
    .stab.index 0 : { *(.stab.index) }
    .stab.indexstr 0 : { *(.stab.indexstr) }
    .comment 0 : { *(.comment) }
    /* DWARF debug sections.
    Symbols in the DWARF debugging sections are relative to the beginning
    of the section so we begin them at 0. */
    /* DWARF 1 */
    .debug 0 : { *(.debug) }
    .line 0 : { *(.line) }
    /* GNU DWARF 1 extensions */
    .debug_srcinfo 0 : { *(.debug_srcinfo) }
    .debug_sfnames 0 : { *(.debug_sfnames) }
    /* DWARF 1.1 and DWARF 2 */
    .debug_aranges 0 : { *(.debug_aranges) }
    .debug_pubnames 0 : { *(.debug_pubnames) }
    /* DWARF 2 */
    .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
    .debug_abbrev 0 : { *(.debug_abbrev) }
    .debug_line 0 : { *(.debug_line) }
    .debug_frame 0 : { *(.debug_frame) }
    .debug_str 0 : { *(.debug_str) }
    .debug_loc 0 : { *(.debug_loc) }
    .debug_macinfo 0 : { *(.debug_macinfo) }
    /* SGI/MIPS DWARF 2 extensions */
    .debug_weaknames 0 : { *(.debug_weaknames) }
    .debug_funcnames 0 : { *(.debug_funcnames) }
    .debug_typenames 0 : { *(.debug_typenames) }
    .debug_varnames 0 : { *(.debug_varnames) }
    .debug_pubtypes 0 : { *(.debug_pubtypes) }
    .debug_ranges 0 : { *(.debug_ranges) }
    /DISCARD/ : { *(.rel.dyn) }
    .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
    /DISCARD/ : { *(.note.GNU-stack) }

    }

Leave a Reply

Your email address will not be published. Required fields are marked *