Minimal semihosted ARM Cortex-M "Hello World"

From D Wiki
Revision as of 12:04, 21 December 2013 by Verax (talk | contribs) (Compiling)
Jump to: navigation, search

The following is an extremely minimal, semihosted "Hello World" D program for ARM Cortex-M processors.

Goals

  • Verify the ARM Cortex-M toolchain (compiler-->linker-->debugger)
  • Demonstrate that a full port of the D runtime and D standard library are not mandatory
  • Demonstrate that linking to C code is not necessary
  • Demonstrate that all required assembly code can be done within D
  • Provide a starting point, with a low barrier to entry, for developers to begin testing the toolchain, porting the D runtime and libraries to the ARM Cortex-M platform, and programming their ARM Cortex-M software in D

Tools

The Code

Program Source Code (start.d)

module start;
 
version(LDC)
{
  import ldc.llvmasm;
}
 
extern(C) __gshared void * _Dmodule_ref;
 
//Must be stored as second 32-bit word in .text section
alias void function() ISR;
extern(C) immutable ISR ResetHandler = &OnReset;
 
void SendCommand(int command, void* message)
{
  version(LDC)
  {
    __asm
    (
      "mov r0, $0;
      mov r1, $1;
      bkpt #0xAB",
      "r,r,~{r0},~{r1}",
      command, message
    );
  }
  else version(GNU)
  {
    asm
    {
      "mov r0, %[cmd]; 
       mov r1, %[msg]; 
       bkpt #0xAB"
	:                              
	: [cmd] "r" command, [msg] "r" message
	: "r0", "r1";
    };
  }
}
 
void OnReset()
{
  while(true)
  {
    // Create semihosting message message
    uint[3] message =
      [
	2, 			      //stderr
	cast(uint)"hello\r\n".ptr,    //ptr to string
	7                             //size of string
      ];

    //Send semihosting command
    SendCommand(0x05, &message);
  }
}

Minimal Runtime Implementation (object.d) (GDC only)

GDC requires the following minimal object.d file in the same folder as start.d. It is imported automatically.

module object;

alias immutable(char)[]  string;

Compiling

For LDC compile with:

ldc2 -march=thumb -mcpu=cortex-m4 -c start.d

For GDC compile with:

arm-none-eabi-gdc -mthumb -mcpu=cortex-m4 -fno-emit-moduleinfo -c -fdata-sections start.d

You will have to change the -mcpu option for your processor accordingly (e.g. cortex-m0, cortex-m3, etc...)

Linker Script (link.ld) (LDC)

NOTE: This linker script was written specifically for an STM32F4 MCU. The MEMORY section and _stackStart will have to be tailored to your hardware.

MEMORY
{
  CCRAM    (rxw) : ORIGIN = 0x10000000, LENGTH =   64k
  SRAM     (rxw) : ORIGIN = 0x20000000, LENGTH =  128k
  FLASH    (rx)  : ORIGIN = 0x08000000, LENGTH = 1024k
}

_stackStart = ORIGIN(CCRAM) + LENGTH(CCRAM);

SECTIONS
{
  .text :
  {
    LONG(_stackStart);              /* Initial stack pointer */
    KEEP(start.o(.data.rel.ro))     /* Interrupt vector table */

    /* the code */
    *(.text)
    *(.text*)

    /* for "hello\r\n" string constant */
    . = ALIGN(4);
    *(.rodata)
    *(.rodata*)
  }>FLASH

  /* Need .data, .bss, .ctors and probably more as program becomes
     More complex */
}

Linking

Linker Script (link.ld) (GDC)

NOTE: This linker script was written specifically for an STM32F4 MCU. The MEMORY section and _stackStart will have to be tailored to your hardware.

/*********************************************************************************************
Memory Spaces Definitions
*********************************************************************************************/
MEMORY
{
  CCRAM    (rxw) : ORIGIN = 0x10000000, LENGTH =   64k
  SRAM     (rxw) : ORIGIN = 0x20000000, LENGTH =  128k
  FLASH    (rx)  : ORIGIN = 0x08000000, LENGTH = 1024k
}

_stackStart = ORIGIN(CCRAM) + LENGTH(CCRAM);

/*********************************************************************************************
 Section Definitions
*********************************************************************************************/
SECTIONS
{  
  .text :       
  {
    LONG(_stackStart);                    /* Initial stack pointer */
    KEEP(start.o(.rodata.ResetHandler))   /* Internet vector table */
    
    /* the code */
    *(.text)      
    *(.text*)  
    
    /* for "hello\r\n" string constant */
    . = ALIGN(4);
    *(.rodata)
    *(.rodata*)
    
  }>FLASH
  
  
  /* Need .data, .bss, .ctors and probably more as program becomes
     More complex */
}

Link with:

arm-none-eabi-ld -T link.ld --gc-sections start.o -o start.elf

Execution

Start OpenOCD:

openocd -f interface/jtag-lock-pick_tiny_2.cfg -f target/stm32f4x.cfg

Start GDB:

arm-none-eabi-gdb start.elf

In GDB, attach to OpenOCD, reset the hardware, load the executable, and begin execution

target remote localhost:3333
monitor arm semihosting enable
monitor reset halt
load
monitor reset init
continue

Output (in OpenOCD window):

hello
hello
...

TODO

  • Modify this software to compile for both GDC and LDC
  • Modify software to include data and bss sections and initialize them in the reset handler (Part 2?)
  • Begin porting the D Runtime to the ARM Cortex-M platform