FAQ:Link

From GNU 68HC11/HC12
(Difference between revisions)
Jump to: navigation, search
(What is the address problem with 68HC12 memory banks?)
(How can I link in several memory banks?)
Line 174: Line 174:
  
 
===How can I link in several memory banks?===
 
===How can I link in several memory banks?===
 +
Release 2.1.1 and 3.x support memory bank switching for 68HC12.
 +
To load correctly your program, your upload program must know the
 +
special mapping that the linker and gdb use.
 +
 +
Basically, everything between 0..0x00ffff must/can be loaded as is (not
 +
paged).
 +
 +
Everything >= 0x10000 is banked and is decomposed in 16K area that must
 +
be put in the correct page.  The page number is given by the formula:
 +
 +
<pre>
 +
(addr - 0x1000000) / 16K
 +
</pre>
 +
 +
(ie, addr 0x1000000 corresponds to page0, 0x1004000 to page1, 0x1008000 to
 +
page2, ...)
 +
 +
The - 0x1000000 is annoying but it is necesary for the current
 +
implementation of the linker so that it links correctly.
 +
 +
To link in several areas, you must define several regions, like:
 +
<pre>
 +
MEMORY
 +
{
 +
  page0 (rwx)    : ORIGIN = 0x0, LENGTH = 256
 +
  text  (rx)    : ORIGIN = 0x0C000, LENGTH = 0x10000 - 0x0C000
 +
  bank0 (rx)    : ORIGIN = 0x1000000, LENGTH = 16K
 +
  bank1 (rx)    : ORIGIN = 0x1004000, LENGTH = 16K
 +
  ...
 +
  data          : ORIGIN = 0x001040, LENGTH = 0x08000 - 0x01040
 +
}
 +
</pre>
 +
And you must manage to put what you want in either <b>text</b> or <b>bankN</b>.
 +
For example:
 +
 +
<pre>
 +
  .text_low  :
 +
  {
 +
    /* Put startup code at beginning so that _start keeps same address.
 +
*/
 +
    /* Startup code.  */
 +
    KEEP (*(.install0)) /* Section should setup the stack
 +
pointer.  */
 +
    KEEP (*(.install1)) /* Place holder for applications.  */
 +
    KEEP (*(.install2)) /* Optional installation of data
 +
sections in RAM.  */
 +
    KEEP (*(.install3)) /* Place holder for applications.  */
 +
    KEEP (*(.install4)) /* Section that calls the main.  */
 +
    KEEP (*(.init))
 +
    ebcs.o(.text)
 +
    date-panel.o(.text)
 +
    main-panel.o(.text)
 +
    info-panel.o(.text)
 +
    debug.o(.text)
 +
  }  > text
 +
</pre>
 +
 +
And put the banked code using:
 +
<pre>
 +
    .bank0 {
 +
      my_file.o(.text)
 +
      my_file.o(.text.*)
 +
      my_file.o(.rodata)
 +
      my_file.o(.rodata.*)
 +
    } > bank0
 +
</pre>
 +
 +
Now, be careful that the current linker does not yet check for function
 +
that cross a page boundary. So, I would recommend to define as many banks
 +
as you need in <b>MEMORY</b> and give them the fixed size 16K.
 +
You can put several files in the same bank as long as it fits.
 +
 
===MC9S12DP256 Addressing Explained===
 
===MC9S12DP256 Addressing Explained===
  

Revision as of 16:43, 18 February 2006

This FAQ section deals with the GNU linker. The linker is the tool that you will use to do the final link and create the final binary based on one or several object files and libraries.

Contents

Mapping Programs to Memory

What is the memory.x?

The memory.x file is used when you use the m68hc11elfb linker script. It defines memory banks that tell the linker where to put the text, data and other program sections. The name of memory banks are specified within the linker script. A typical memory.x file defines the MEMORY banks as follows:

MEMORY
{
  page0 (rwx) : ORIGIN = 0x0, LENGTH = 256
  text  (rx)  : ORIGIN = 0xE000, LENGTH = 2048
  data        : ORIGIN = 0x0, LENGTH = 0
}

This definition creates 3 memory banks:

  • page0 represents a the HC11/HC12 region for direct addressing mode. This memory bank must always be completely included in the [0..0x0ff] interval.
  • text represents the memory bank where the linker will put the .text sections which contains the code. In most cases it represents the ROM of the board (but it can also represent RAM).
  • data represents the memory bank used to put the initialized and non-initialized data. This bank usually represents RAM.

See Also How does the linker know where memory.x is?

How does the linker know where memory.x is?

The memory.x file is searched in the current directory and in all directories specified with the -L switch. There is also a default directory which is /usr/m6811-elf/lib or xxx\m6811-elf\lib.

For example, in GEL, the memory.x file is read from the board specific config directory. This is caused by the option:

-L$(GEL_BASEDIR)/config/$(TARGET_BOARD)

For example:

-L/opt/gel-hc1x-1.4.2/config/m68hc11-32k

and this configuration directory contains the memory.x file that corresponds to the m68hc11-32k generic board.

See Also What is the memory.x?

What does "region text is full" mean?

This error occurs when the program is too big to fit in the memory you specified in memory.x. I don't have your memory.x so I can't tell what is your limit but in the following example:

MEMORY
{
  page0 (rwx) : ORIGIN = 0x0, LENGTH = 256
  /* Board with 32K ROM.  */
  text  (rx)  : ORIGIN = 0x8000, LENGTH = 0x8000

  /* And 32K RAM. */
  data : ORIGIN = 0x1040, LENGTH = 0x8000-0x1040
}

the program memory is limited by the text region which is 32Kb.

This can happen if you try to link with too many files (whose total exceeds your text region size). It is often the case if you use the Newlib or use floating point operations. The -mshort and -fshort-double may help in reducing that but in many cases this is not enough.

The printf() which comes with Newlib is pretty big and as far as I'm concerned I prefer not to use it. However, it is useful for gcc validation.

Some tips:

  • Try compiling with -mshort -fshort-double -Os
  • Consider using linker relaxation with -mrelax
  • Try using garbage collection of sections
  • Avoid using Newlib and printf

See Also What is the memory.x? How can I remove unused functions? How do I tell the compiler to use direct addressing mode to access data

How can I put the soft registers in a specific section?

The soft registers are defined in the section .softregs. During the final link, this section is merged with either the .page0 section (68HC11) or the .bss section (68HC12).

You can change this by using your own linker script and putting the following definition:

 *(.softregs)

In the section you want.

For example, copy /usr/m6811-elf/lib/ldscripts/m68hc11elfb.x to your project.

Change:

.page0 :
 {
   *(.page0)
   *(.softregs)
 }  > page0

Into:

.page0 :
 {
   *(.page0)
 }  > page0

And add the *(.softregs) somewhere in .bss for example, like:

 .bss   :
 {
   __bss_start = .;
   *(.softregs)
   *(.sbss)
   *(.scommon)
   *(.dynbss)
   *(.bss)
   *(.bss.*)
   *(.gnu.linkonce.b.*)
   *(COMMON)
   PROVIDE (_end = .);
 }  > data

And link with your own linker script with: -Wl,--script,myscript.ld

Be sure to compile with -mrelax otherwise you might have errors during the link (addresses truncated because too big for direct addressing modes).

What is the page0 concept?

Some of this may just be my opinion, but...

The register space of the 68HC12 processor family (0x0 to 0x1ff) is not actually tied to the page0 concept. "page0" refers to the address space (an address which only uses the low 8 bits of the address. The address range in page0 is therefore 0x00 to 0xff. This only has meaning because certain optimizations are only possible if it is assumed that the address is only 8 bits, so it fits in a smaller instruction. Since HC12 has a complete 16-bit bus, this optimization is less significant. I do agree that it could make sens if page0 was only defined to use part of page0 space (such as LENGTH=0x80) since the space included is page0. The problem would arise if any space outside (over 0xff) was trying to act like page 0 space. Even then, I realize nobody might notice unless the compiler/linker was confused by this wild claim.

With HC12, you can still optimize by using page0. One difference is it has by default, hardware registers instead of scratch RAM in page0. In HC11, page0 ram was critical because it had to access fast variables often to work with 16-bit values. Now that HC12 can work with that more in mcu registers, it can further optimize speed by accessing most hardware registers in page0. You noticed the register size exceeds page0, but hopefully the more critical registers made it into the first 256 bytes. Also, since the MCU is meant for working i/o more than doing software calculations, this sounds more optimal anyway.

The purpose of the page0 region in "memory.x" is primarily for a space to place the critical variables in HC11, into the page0 ram. In HC12, it is possible the compiler/linker does not even use it. Since the default ldscript does not place .page0 variables into the page0 region, the region might be unused. I have deleted it and my project still worked, but I decided I should leave it there in case some new or unseen feature does use it later, or until the code manager (Stephane, or whoever works on gnu-m68hc1x) states that it can be removed. Besides, I think it's better to do less work to keep something working than to do more work to risk breaking it.

An important question is how to access those registers, then. If you have been placing their variable names and types into page0, then perhaps it is good enough to simply use a different region name, such as "hwreg", instead of "page0". If you still have the page0 region defined in the same place, it might overlap. You would have to delete the page0 line in "memory.x". The GEL example seems to recommend a more versatile method. Note that it does not even use a memory region for this. It defines a symbol to the linker, with the start addr of registers, called "_io_ports" (see src/libbsp/ports.s). In a C header, this is defined to a type (volatile unsigned char), which can then be accessed as an array. It can also be type cast to (volatile unsigned short) for 16-bit registers. This is the way I am acessing it.

Thanks for the input on this subject. --jeffs

Jefferson Smith imajeffs -- at -- hotmail.com

See Also What is the memory.x?

Memory Banks

What is the address problem with 68HC12 memory banks?

It is very easy to make gcc generate a CALL and use RTC. This can be driven by some option (in 68HC12 mode).

Now, the big problem is the linker which must handle the 24-bit address space. It must do so with a paging notion and an overlapping notion (of the 16K window). Today, the linker is only able to manage contiguous address space (I mean that it puts *.text code together and builds a VMA address incrementally without holes). In other words, it is able to build something that maps (loggically) to:

          0x8000   0xC000   0x10000    0x14000    0x18000
----------+--------+--------+----------+---------+------+
          |        |        |          |         |      |
----------+--------+--------+----------+---------+------+
          <------------------ text section ------------->

Some kind of address translation is necessary.

Let's say that [0x08000..0x0bfff] is page 0
	       [0x0C000..0x0ffff] is page 1
	       [0x10000..0x14000] is page 2
	       [0x14000..0x18000] is page 3   (from a GNU linker VMA point of view)

It is clear that from 68HC12 VMA point of view, when page 1 is installed, it is not at [0x0C000..0x0ffff] but really at [0x08000..0x0bfff]. But the linker does not know that and there is no easy way to tell him.

To give another example, a function can be defined at 0x10000 from GNU ld point of view. When a call to that function will be made, if there is not address translation, the call will use page 2 and 0x0000 as address. And this is not in the program bank window.

How can I link in several memory banks?

Release 2.1.1 and 3.x support memory bank switching for 68HC12. To load correctly your program, your upload program must know the special mapping that the linker and gdb use.

Basically, everything between 0..0x00ffff must/can be loaded as is (not paged).

Everything >= 0x10000 is banked and is decomposed in 16K area that must be put in the correct page. The page number is given by the formula:

(addr - 0x1000000) / 16K

(ie, addr 0x1000000 corresponds to page0, 0x1004000 to page1, 0x1008000 to page2, ...)

The - 0x1000000 is annoying but it is necesary for the current implementation of the linker so that it links correctly.

To link in several areas, you must define several regions, like:

MEMORY
{
   page0 (rwx)    : ORIGIN = 0x0, LENGTH = 256
   text  (rx)     : ORIGIN = 0x0C000, LENGTH = 0x10000 - 0x0C000
   bank0 (rx)     : ORIGIN = 0x1000000, LENGTH = 16K
   bank1 (rx)     : ORIGIN = 0x1004000, LENGTH = 16K
   ...
   data           : ORIGIN = 0x001040, LENGTH = 0x08000 - 0x01040
}

And you must manage to put what you want in either text or bankN. For example:

   .text_low  :
   {
     /* Put startup code at beginning so that _start keeps same address.
*/
     /* Startup code.  */
     KEEP (*(.install0))	/* Section should setup the stack
pointer.  */
     KEEP (*(.install1))	/* Place holder for applications.  */
     KEEP (*(.install2))	/* Optional installation of data
sections in RAM.  */
     KEEP (*(.install3))	/* Place holder for applications.  */
     KEEP (*(.install4))	/* Section that calls the main.  */
     KEEP (*(.init))
     ebcs.o(.text)
     date-panel.o(.text)
     main-panel.o(.text)
     info-panel.o(.text)
     debug.o(.text)
   }  > text

And put the banked code using:

    .bank0 {
      my_file.o(.text)
      my_file.o(.text.*)
      my_file.o(.rodata)
      my_file.o(.rodata.*)
    } > bank0

Now, be careful that the current linker does not yet check for function that cross a page boundary. So, I would recommend to define as many banks as you need in MEMORY and give them the fixed size 16K. You can put several files in the same bank as long as it fits.

MC9S12DP256 Addressing Explained

Optimizations

How do I tell the compiler to use direct addressing mode to access data

How can I remove unused functions?

Misc

Where is _io_ports defined?

What does "linking files compiled for 16-bit integers and others for 32-bit integers" mean?

What is the crt0 and how can I remove it?

Personal tools