Welcome, guest ( Login )

WikiHome » LoadingCodeSections

LoadingCodeSections

Version 4, changed by admin. 04/25/2007.   Show version history

Loading Code Sections
 

Abstract
By default, the software tool chain for the Nios® II processor loads all the executable code into one section.  For most users, this is fine; however, there are cases where you want the code to be loaded in different sections.  For example, you may want most of your code to run out of SDRAM, but some critical code needs to be run out of on-chip memory or Tightly Coupled Memory. .exceptions code is an example of this. The standard loader that comes with the Nios II processor will not accommodate this without some help. This example will look at the .exceptions case, but it could apply to any section you wish to create.
 

How it is done

To make this work, there are two things that have to take place. First, the linker has to be instructed to build the .exceptions section at the exceptions memory location but stored with the rest of the code like the .text section.
Next, a loader has to be run on startup that can load the section from the logical location to the “Real” location.

 
Changing the linker

The easiest way to create a custom linker script is to start with the generated.x linker script, which the IDE generates, and customize it. Generated.x will be created the first time you build your system_library project. After it has been created, make a copy of the generated.x (found in the syslib/Debug or Relase/system_description) and place it in the top level of the syslib project.  Feel free to change the name if you like.  Then in the syslib properties page, in the IDE, make sure you select custom link and point it to your new linker script.


 
Open up the linker script with your favorite text editor.  This example assumes that we are relocating the .exceptions section only, and leaving everything else the same.  You may have different needs, so this can be adapted to what you need.
 
In this example, there is a memory called Exceptions, which is where the SOPC Builder has been told the exception vector resides.  The system is configured to have code loaded in the SDRAM, and the Nios II processor boots from an on-chip boot memory.  Appendix A has the original linker script, and Appendix B has the changed script.
 
The basic idea is to move the entire .excpections section below the .rodata section  (it could be right after the .text section as well).  Then add the following code:
 
            .exceptions :AT (LOADADDR (.rodata) + SIZEOF (.rodata))
 
 
This tells the linker to put the code right after the .rodata section, even though it is targeted to be in the Exceptions memory.
 
Additionally the .rwdata needs to be moved to be below the new .exceptions sections.  (if you don’t, the linker will try and put the .exceptions and the next section on top of each other).
 
 
.rwdata (LOADADDR(.exceptions)+ SIZEOF (.exceptions)) :
 
Now the .rwdata section will be located right after the .exceptions section.
 
One other thing I did was to change the entry to be in SDRAM as well:
 
    .entry :
    {
        KEEP (*(.entry))
    } > sdram
 
 
This is not strictly necessary, but it creates a unified package.
 
Now the linker will put the code in the right place.
 

 
Getting the code copied

The in the crt0.S code, there is a load_section code that will load code into different physical memories.  This is very useful.  If you have you own custom section, you will need to modify this code. Otherwise it is just a matter of enabling the code.
 
Here is the code:
 
#ifdef ALT_NO_BOOTLOADER
#if ALT_LOAD_SECTIONS
 
#ifdef ALT_STACK_CHECK
 
    mov   et, zero
 
#endif
 
    call alt_load
 
#endif /* ALT_LOAD_SECTIONS */
#endif /* ALT_NO_BOOTLOADER */
 
To make this work, we need to define  ALT_NO_BOOTLOADER  in the syslib c/c++ Build settings


 
 
 
Once this is done, recompile and it will work.  What happens is the alt_load function copies the .exception code to the right spot during startup and before the interrupts are needed.

 

Apendix A

/* generated.x
 *
 * Machine generated for a CPU named "cpu" as defined in:
 * C:\\\\\\\\altera\\\\\\\\kits\\\\\\\\nios2_51\\\\\\\\examples\\\\\\\\verilog\\\\\\\\niosII_cyclone_1c20\\\\\\\\startupstandard\\\\\\\\software\\\\\\\\hello_world_small_1_syslib\\\\\\\\..\\\\\\\\..\\\\\\\\std_1c20.ptf
 *
 * Generated: 2006-01-25 21:04:12.273
 *
 */
 
/*
 
DO NOT MODIFY THIS FILE
 
   Changing this file will have subtle consequences
   which will almost certainly lead to a nonfunctioning
   system. If you do modify this file, be aware that your
   changes will be overwritten and lost when this file
   is generated again.
 
DO NOT MODIFY THIS FILE
 
*/
 
MEMORY
{
    reset : ORIGIN = 0x00802000, LENGTH = 32
    ext_flash : ORIGIN = 0x00000000, LENGTH = 8388608
    ext_ram : ORIGIN = 0x02000000, LENGTH = 1048576
    epcs_controller : ORIGIN = 0x02100000, LENGTH = 2048
    sdram : ORIGIN = 0x01000000, LENGTH = 16777216
    boot : ORIGIN = 0x00802020, LENGTH = 8160
    Exception_UNUSED : ORIGIN = 0x00800800, LENGTH = 32
    Exception : ORIGIN = 0x00800820, LENGTH = 2016
}
 
    /* Define symbols for each memory base-address */
 __alt_mem_ext_flash = 0x00000000 ;
 __alt_mem_ext_ram = 0x02000000 ;
 __alt_mem_epcs_controller = 0x02100000 ;
 __alt_mem_sdram = 0x01000000 ;
 __alt_mem_boot = 0x00802000 ;
 __alt_mem_Exception = 0x00800800 ;
 
 
 
OUTPUT_FORMAT( "elf32-littlenios2",
               "elf32-littlenios2",
               "elf32-littlenios2" )
OUTPUT_ARCH( nios2 )
ENTRY( _start )
 
/* Do we need any of these for elf?
   __DYNAMIC = 0;
 */
 
SECTIONS
{
    .entry :
    {
        KEEP (*(.entry))
    } > reset
 
    .exceptions :
    {
        PROVIDE (__ram_exceptions_start = ABSOLUTE(.));
        . = ALIGN(0x20);
        *(.irq)
        KEEP (*(.exceptions.entry.label));
        KEEP (*(.exceptions.entry.user));
        KEEP (*(.exceptions.entry));
        KEEP (*(.exceptions.irqtest.user));
        KEEP (*(.exceptions.irqtest));
        KEEP (*(.exceptions.irqhandler.user));
        KEEP (*(.exceptions.irqhandler));
        KEEP (*(.exceptions.irqreturn.user));
        KEEP (*(.exceptions.irqreturn));
        KEEP (*(.exceptions.notirq.label));
        KEEP (*(.exceptions.notirq.user));
        KEEP (*(.exceptions.notirq));
        KEEP (*(.exceptions.soft.user));
        KEEP (*(.exceptions.soft));
        KEEP (*(.exceptions.unknown.user));
        KEEP (*(.exceptions.unknown));
        KEEP (*(.exceptions.exit.label));
        KEEP (*(.exceptions.exit.user));
        KEEP (*(.exceptions.exit));
        KEEP (*(.exceptions));
        PROVIDE (__ram_exceptions_end = ABSOLUTE(.));
    } > Exception
 
    PROVIDE (__flash_exceptions_start = LOADADDR(.exceptions));
 
    .text :
    {
        /*
         * All code sections are merged into the text output section, along with
         * the read only data sections.
         *
         */
 
        PROVIDE (stext = ABSOLUTE(.));
 
        *(.interp)
        *(.hash)
        *(.dynsym)
        *(.dynstr)
        *(.gnu.version)
        *(.gnu.version_d)
        *(.gnu.version_r)
        *(.rel.init)
        *(.rela.init)
        *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
        *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
        *(.rel.fini)
        *(.rela.fini)
        *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
        *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
        *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
        *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
        *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
        *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
        *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
        *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
        *(.rel.ctors)
        *(.rela.ctors)
        *(.rel.dtors)
        *(.rela.dtors)
        *(.rel.got)
        *(.rela.got)
        *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
        *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
        *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
        *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
        *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
        *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
        *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
        *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
        *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
        *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
        *(.rel.plt)
        *(.rela.plt)
 
        KEEP (*(.init))
        *(.plt)
        *(.text .stub .text.* .gnu.linkonce.t.*)
 
        /* .gnu.warning sections are handled specially by elf32.em.  */
 
        *(.gnu.warning.*)
        KEEP (*(.fini))
        PROVIDE (__etext = ABSOLUTE(.));
        PROVIDE (_etext = ABSOLUTE(.));
        PROVIDE (etext = ABSOLUTE(.));
 
        *(.eh_frame_hdr)
        /* Ensure the __preinit_array_start label is properly aligned.  We
           could instead move the label definition inside the section, but
           the linker would then create the section even if it turns out to
           be empty, which isn't pretty.  */
        . = ALIGN(32 / 8);
        PROVIDE (__preinit_array_start = ABSOLUTE(.));
        *(.preinit_array)
        PROVIDE (__preinit_array_end = ABSOLUTE(.));
        PROVIDE (__init_array_start = ABSOLUTE(.));
        *(.init_array)
        PROVIDE (__init_array_end = ABSOLUTE(.));
        PROVIDE (__fini_array_start = ABSOLUTE(.));
        *(.fini_array)
        PROVIDE (__fini_array_end = ABSOLUTE(.));
        SORT(CONSTRUCTORS)
        KEEP (*(.eh_frame))
        *(.gcc_except_table)
        *(.dynamic)
        PROVIDE (__CTOR_LIST__ = ABSOLUTE(.));
        KEEP (*(.ctors))
        KEEP (*(SORT(.ctors.*)))
        PROVIDE (__CTOR_END__ = ABSOLUTE(.));
        PROVIDE (__DTOR_LIST__ = ABSOLUTE(.));
        KEEP (*(.dtors))
        KEEP (*(SORT(.dtors.*)))
        PROVIDE (__DTOR_END__ = ABSOLUTE(.));
        KEEP (*(.jcr))
        . = ALIGN(32 / 8);
    } >  sdram =0x3a880100 /* NOP on Nios2 (big endian) */
 
    .rodata :
    {
        PROVIDE (__ram_rodata_start = ABSOLUTE(.));
        . = ALIGN(32 / 8);
        *(.rodata .rodata.* .gnu.linkonce.r.*)
        *(.rodata1)
        . = ALIGN(32 / 8);
        PROVIDE (__ram_rodata_end = ABSOLUTE(.));
    } > sdram
 
    PROVIDE (__flash_rodata_start = LOADADDR(.rodata));
 
    .rwdata  :
    {
        PROVIDE (__ram_rwdata_start = ABSOLUTE(.));
        . = ALIGN(32 / 8);
        *(.got.plt) *(.got)
        *(.data1)
        *(.data .data.* .gnu.linkonce.d.*)
 
        _gp = ABSOLUTE(. + 0x8000);
        PROVIDE(gp = _gp);
 
        *(.sdata .sdata.* .gnu.linkonce.s.*)
        *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
 
        . = ALIGN(32 / 8);
        _edata = ABSOLUTE(.);
        PROVIDE (edata = ABSOLUTE(.));
        PROVIDE (__ram_rwdata_end = ABSOLUTE(.));
    } > sdram
 
    PROVIDE (__flash_rwdata_start = LOADADDR(.rwdata));
 
    .bss :
    {
        __bss_start = ABSOLUTE(.);
        PROVIDE (__sbss_start = ABSOLUTE(.));
        PROVIDE (___sbss_start = ABSOLUTE(.));
 
        *(.dynsbss)
        *(.sbss .sbss.* .gnu.linkonce.sb.*)
        *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
        *(.scommon)
 
        PROVIDE (__sbss_end = ABSOLUTE(.));
        PROVIDE (___sbss_end = ABSOLUTE(.));
 
        *(.dynbss)
        *(.bss .bss.* .gnu.linkonce.b.*)
        *(COMMON)
 
        . = ALIGN(32 / 8);
        __bss_end = ABSOLUTE(.);
    } > sdram
 
    /*
     * One output section for each of the available partitions. These are not
     * used by default, but can be used by users applications using the .section
     * directive.
     *
     * The memory partition used for the heap is treated in  special way, i.e. a
     * symbol is added to point to the heap start.
     *
     * Note that when running from flash, these sections are not loaded by the
     * HAL.
     *
     */
 
    .ext_flash :
    {
        PROVIDE (_alt_partition_ext_flash_start = ABSOLUTE(.));
        *(.ext_flash .ext_flash.*)
        . = ALIGN(32 / 8);
        PROVIDE (_alt_partition_ext_flash_end = ABSOLUTE(.));
    } > ext_flash
 
    PROVIDE (_alt_partition_ext_flash_load_addr = LOADADDR(.ext_flash));
 
    .ext_ram :
    {
        PROVIDE (_alt_partition_ext_ram_start = ABSOLUTE(.));
        *(.ext_ram .ext_ram.*)
        . = ALIGN(32 / 8);
        PROVIDE (_alt_partition_ext_ram_end = ABSOLUTE(.));
    } > ext_ram
 
    PROVIDE (_alt_partition_ext_ram_load_addr = LOADADDR(.ext_ram));
 
    .epcs_controller :
    {
        PROVIDE (_alt_partition_epcs_controller_start = ABSOLUTE(.));
        *(.epcs_controller .epcs_controller.*)
        . = ALIGN(32 / 8);
        PROVIDE (_alt_partition_epcs_controller_end = ABSOLUTE(.));
    } > epcs_controller
 
    PROVIDE (_alt_partition_epcs_controller_load_addr = LOADADDR(.epcs_controller));
 
    .sdram :
    {
        PROVIDE (_alt_partition_sdram_start = ABSOLUTE(.));
        *(.sdram .sdram.*)
        . = ALIGN(32 / 8);
        PROVIDE (_alt_partition_sdram_end = ABSOLUTE(.));
        _end = ABSOLUTE(.);
        end = ABSOLUTE(.);
    } > sdram
 
    PROVIDE (_alt_partition_sdram_load_addr = LOADADDR(.sdram));
 
    .boot :
    {
        PROVIDE (_alt_partition_boot_start = ABSOLUTE(.));
        *(.boot .boot.*)
        . = ALIGN(32 / 8);
        PROVIDE (_alt_partition_boot_end = ABSOLUTE(.));
    } > boot
 
    PROVIDE (_alt_partition_boot_load_addr = LOADADDR(.boot));
 
    .Exception :
    {
        PROVIDE (_alt_partition_Exception_start = ABSOLUTE(.));
        *(.Exception .Exception.*)
        . = ALIGN(32 / 8);
        PROVIDE (_alt_partition_Exception_end = ABSOLUTE(.));
    } > Exception
 
    PROVIDE (_alt_partition_Exception_load_addr = LOADADDR(.Exception));
 
    /*
     * 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) }
 
    /* Altera debug extensions */
    .debug_alt_sim_info 0 : { *(.debug_alt_sim_info) }
}
/* provide a pointer for the stack */
 
/*
 * Don't override this, override the __alt_stack_* symbols instead.
 */
__alt_data_end = 0x02000000;
 
/*
 * The next two symbols define the location of the default stack.  You can
 * override them to move the stack to a different memory.
 */
PROVIDE( __alt_stack_pointer = __alt_data_end );
PROVIDE( __alt_stack_limit   = _end );
 
/*
 * This symbol controls where the start of the heap is.  If the stack is
 * contiguous with the heap then the stack will contract as memory is
 * allocated to the heap.
 * Override this symbol to put the heap in a different memory.
 */
PROVIDE( __alt_heap_start    = end );
 
 
 
 
 
 

 
 
Appendix B
 
/* generated.x
 *
 * Machine generated for a CPU named "cpu" as defined in:
 * C:\\\\\\\\altera\\\\\\\\kits\\\\\\\\nios2_51\\\\\\\\examples\\\\\\\\verilog\\\\\\\\niosII_cyclone_1c20\\\\\\\\startupstandard\\\\\\\\software\\\\\\\\hello_world_small_1_syslib\\\\\\\\..\\\\\\\\..\\\\\\\\std_1c20.ptf
 *
 * Generated: 2006-01-25 21:04:12.273
 *
 */
 
/*
 
DO NOT MODIFY THIS FILE
 
   Changing this file will have subtle consequences
   which will almost certainly lead to a nonfunctioning
   system. If you do modify this file, be aware that your
   changes will be overwritten and lost when this file
   is generated again.
 
DO NOT MODIFY THIS FILE
 
*/
 
MEMORY
{
    reset : ORIGIN = 0x00802000, LENGTH = 32
    ext_flash : ORIGIN = 0x00000000, LENGTH = 8388608
    ext_ram : ORIGIN = 0x02000000, LENGTH = 1048576
    epcs_controller : ORIGIN = 0x02100000, LENGTH = 2048
    sdram : ORIGIN = 0x01000000, LENGTH = 16777216
    boot : ORIGIN = 0x00802020, LENGTH = 8160
    Exception_UNUSED : ORIGIN = 0x00800800, LENGTH = 32
    Exception : ORIGIN = 0x00800820, LENGTH = 2016
}
 
    /* Define symbols for each memory base-address */
 __alt_mem_ext_flash = 0x00000000 ;
 __alt_mem_ext_ram = 0x02000000 ;
 __alt_mem_epcs_controller = 0x02100000 ;
 __alt_mem_sdram = 0x01000000 ;
 __alt_mem_boot = 0x00802000 ;
 __alt_mem_Exception = 0x00800800 ;
 
 
 
OUTPUT_FORMAT( "elf32-littlenios2",
               "elf32-littlenios2",
               "elf32-littlenios2" )
OUTPUT_ARCH( nios2 )
ENTRY( _start )
 
/* Do we need any of these for elf?
   __DYNAMIC = 0;
 */
 
SECTIONS
{
    .entry :
    {
        KEEP (*(.entry))
    } > sdram
 
 
    .text :
    {
        /*
         * All code sections are merged into the text output section, along with
         * the read only data sections.
         *
         */
 
        PROVIDE (stext = ABSOLUTE(.));
 
        *(.interp)
        *(.hash)
        *(.dynsym)