HC9S12DG128

From Arctic Core - the open source AUTOSAR embedded platform

Jump to: navigation, search

Contents

Arctic Studio Setup

Compiler

To build for the HCS12 you will need a cross-compiler.

  • It can be downloaded from here: m6811-elf.zip
  • Unzip it into cygwin/opt/ in your Arctic Studio install directory.
    • The structure should end up looking like this:
opt
'--- m6811-elf
     |--- bin
     |--- include
     |--- info
     |--- lib
     |--- m6811-elf
     |--- man
     |--- share

Examples

The tiny example (examples/tiny) is compatible with the HCS12 port. If running another project or example, make sure to edit the linkscript (arch/hc1x/hcs12d/scripts/linkscript_gcc.ldf) to map the code in memory.

Settings

To configure the toolchain in Studio:

  • Go to Properties on the project you want to build
  • Under C/C++ Build --> Environment, set
    • BOARDDIR = hcs12_elmicro_card12
    • CROSS_COMPILE = /opt/m6811-elf/bin/m6811-elf-

Memory

16 bit -> 64k address space, combined for regs, ram, eeprom, flash. 128k flash available as 8 * 16k banks. Two banks are permanently mapped in address space, and the others are available through a bank window.

Map

0x0000
        registers, RAM, EEPROM (mapped at runtime)
0x4000
        fixed flash 1, 16k
0x8000
        bank window, 8*16k
0xc000
        fixed flash 2, 16k
0xffff

Flash module banks

The theoretical maximum of memory banks on the 68HC12 is 256 * 16k = 4MB. The MC9S12DG128 uses banks 8-15 of the 256.

name       id     mapping
-----------------------------
bank 8     38        -
bank 9     39        -
bank 10    3a        -
bank 11    3b        -
bank 12    3c        -
bank 13    3d        -
bank 14    3e   fixed flash 1
bank 15    3f   fixed flash 2

Runtime

At runtime a cpu register called PPAGE selects which bank is shown in the bank window. The register is located at 0x0030 and is writable in bits 5:0. The banking scheme presents some challenges:

  • branching to/calling code in banked memory
  • reading data from banked memory

GNU compiler

The 68HC1x port of GCC supports compiling code that uses banked memory.

On the 68HC11, jumps that require bank switching are implemented by generation of so called trampoline functions. These functions are atomic and reside in un-banked memory. They are linked in between the caller and the callee and contain code that writes to PPAGE and then jumps to the originally called address.

On the 68HC12, specific atomic operations for performing this task have been added to the instruction set. The operations are CALL and RTC and are the banked memory equivalents of JSR and RTS respectively.

GCC can be told to compile to either one of these operations locally in code by adding the attributes near and far to functions.

extern void __attribute__((near)) f (void);
extern void __attribute__((far))  f (void);

A function with the attribute far is called with CALL (or through a trampoline if using 68HC11) and returns with RTC.

To make all calls far, give the following option to GCC

-mlong-calls

GNU linker script

The linker needs to fill in bank numbers as well as addresses in the compiled code. This is done by a convention where all of the memory is unfolded in a large virtual space.

There are two separate concepts concerning memory addresses:

VMA (Virtual Memory Address)
Addresses that are used in the code
LMA (Load Memory Address)
Addresses that specify where in memory that things are loaded

These addresses are often equal, but as we will see, they will differ when dealing with banked memory.

A common example of differing VMA and LMA is for initialized data placed in RAM. To make RAM data persistent in the sense that it is initialized with the same values on each reset, the data is placed in a section of flash and copied to RAM at boot time by program code. Still, references to this data from other parts of program code must point to the RAM. Thus in this example the data section will have a VMA -> some RAM address, and a LMA -> some flash address.

On top of this, on 68HC1x, both memory address specifications run in an expanded range that is larger than 64k (16bit)

VMA

For VMA:s the linker uses these formulas to make HW addresses and bank numbers from specified addresses:

if sym_addr >= sym_bank_base then
  hw_addr = ((sym_addr - sym_bank_base) % hw_bank_size) + hw_bank_window_base
  hw_bank = ((sym_addr - sym_bank_base) / hw_bank_size)
else
  hw_addr = sym_addr
  hw_bank = 0
end if;
 

where

hw_addr
is the actual address used on the hw,
hw_bank
is the bank number used for PPAGE,
hw_bank_window_base = 0x8000
is the start address of the bank window,
hw_bank_size = 0x4000 (16k)
is the size of the banks,
sym_addr
is the address specified in the linker script,
sym_bank_base = 0x0d0000
is the base address for the banks in the virtual expansion

This means that to specify VMA:s for our banks 8-15 we put the following in our link script:

MEMORY
{
  bank8  (rx)   : ORIGIN = 0x0f0000, LENGTH = 16k  /* hw_addr: 0x8000, hw_bank: 8 */
  bank9  (rx)   : ORIGIN = 0x0f4000, LENGTH = 16k  /* hw_addr: 0x8000, hw_bank: 9 */
  bank10 (rx)   : ORIGIN = 0x0f8000, LENGTH = 16k  /* hw_addr: 0x8000, hw_bank: a */
  bank11 (rx)   : ORIGIN = 0x0fc000, LENGTH = 16k  /* hw_addr: 0x8000, hw_bank: b */
  bank12 (rx)   : ORIGIN = 0x100000, LENGTH = 16k  /* hw_addr: 0x8000, hw_bank: c */
  bank13 (rx)   : ORIGIN = 0x104000, LENGTH = 16k  /* hw_addr: 0x8000, hw_bank: d */
  bank14 (rx)   : ORIGIN = 0x108000, LENGTH = 16k  /* hw_addr: 0x8000, hw_bank: e */
  bank15 (rx)   : ORIGIN = 0x10c000, LENGTH = 16k-0x100  /* hw_addr: 0x8000, hw_bank: f         */
                                                         /* leaving space for vector table here */
}

LMA

For LMA:s, the addressing scheme is used by the downloader to put the data in the correct banks in the flash (therefore I assume that this scheme could differ between different downloaders, but I'm not sure. I got it working with s19 format downloaded using Code Warrior debugger over TBDML)

LMA:s for the banks are specified like this:

0x<bank id>8000

We add this to the MEMORY section in the link script

  bank8_lma  (rx)   : ORIGIN = 0x388000, LENGTH = 16k
  bank9_lma  (rx)   : ORIGIN = 0x398000, LENGTH = 16k
  bank10_lma (rx)   : ORIGIN = 0x3a8000, LENGTH = 16k
  bank11_lma (rx)   : ORIGIN = 0x3b8000, LENGTH = 16k
  bank12_lma (rx)   : ORIGIN = 0x3c8000, LENGTH = 16k
  bank13_lma (rx)   : ORIGIN = 0x3d8000, LENGTH = 16k
  bank14_lma (rx)   : ORIGIN = 0x3e8000, LENGTH = 16k
  bank15_lma (rx)   : ORIGIN = 0x3f8000, LENGTH = 16k-0x100
  vectors_lma (rx)  : ORIGIN = 0x3fff80, LENGTH = 0x80

Placing code and data

NOTE
The GNU linker can't place code into the banks automatically, so things that are added will end up in the default section (.text) which is loaded into the fixed area at 0x4000 - 0x8000. Since this space is limited, manual placement by editing the linker script is probably needed whenever adding code. Removing code that is already mapped in the linker script will generate a link-time error.

Strategy

Memory layout strategy:

0x0000
        Registers
0x2000
        RAM: 
          data (loaded into 0xc000+ and copied at init), uninitialized data (cleared at init)
0x4000
        Code (that needs to be un-banked): 
          init, interrupts, some library code
0x8000
        Code (into banks): 
          platform, drivers, application
0xc000
        Read-only data: 
          ram image, configuration etc.
0xffff


Link script

There are many ways to put the appropriate addresses into your binary and I will show a way that worked for me

I define a section for each bank, and put stuff marked with the bank name in the code in to it. I also manually place object code into the sections.

.bank8 :
{
  *(.bank8)
  my_obj.o(.text)
  . = ALIGN(2);
} > bank8 AT>bank8_lma =0xff
 
.bank9 :
{
  *(.bank9)
  . = ALIGN(2);
} > bank9 AT>bank9_lma  =0xff
 
...

The > name operator tells ld to set the section VMA to be the memory area name. The AT> name operator tells ld to set the section LMA to be the memory area name.

Mapping RAM, registers and EEPROM

These memory sections are mapped into the memory range at runtime.

Setting INITRM, INITRG, INITEE

.section ".install1"
	movb #0x39,0x0010 ;INITRM
	movb #0x00,0x0011 ;INITRG
	movb #0x09,0x0012 ;INITEE

TODO: What exactly does this do?

If memory sections overlap in the on-chip system memory space, the precedence is defined as follows:

— Highest —
On-chip registers (usually $0000 or $1000)
BDM ROM (only when BDM active)
On-chip RAM
On-chip EEPROM
On-chip program memory (FLASH or ROM)
Expansion windows (on MCUs with expanded memory)
Other external memory
— Lowest —

Debuggers

winIDEA

Settings:

  • Emulation Options
    • iCARD : 68HC12
    • CPU : MC9S12DG128
  • CPU Setup
    • Operating mode : Special (important)
    • Breakpoints : Software
    • Use this clock after RESET : 16000
  • FLASH Programming Setup
    • Through FLASH monitor
    • Download Files (.elf)
      • Load Code from : Program Header / Physical
  • Files for download (.elf)
    • Load Code from : Program Header / Physical
    • Load Code : no
    • Load Symbols : yes
  • Debug Options
    • Directories
      • Convert source file paths : \cygdrive\c=c: