Difference between revisions of "Minimal semihosted ARM Cortex-M "Hello World""

From D Wiki
Jump to: navigation, search
(Compiling)
(Compiling)
Line 86: Line 86:
 
=Compiling=
 
=Compiling=
 
NOTE: You will have to change the -mcpu option for your processor accordingly (e.g. cortex-m0, cortex-m3, etc...)
 
NOTE: You will have to change the -mcpu option for your processor accordingly (e.g. cortex-m0, cortex-m3, etc...)
For LDC compile with:
+
 
 +
===For LDC compile with===
 
<source lang="html4strict">
 
<source lang="html4strict">
 
ldc2 -march=thumb -mcpu=cortex-m4 -c start.d
 
ldc2 -march=thumb -mcpu=cortex-m4 -c start.d
Line 92: Line 93:
 
For LDC, object.d must NOT exist, or it will try to import it automatically.
 
For LDC, object.d must NOT exist, or it will try to import it automatically.
  
For GDC compile with:
+
===For GDC compile with===
 
<source lang="html4strict">
 
<source lang="html4strict">
 
arm-none-eabi-gdc -mthumb -mcpu=cortex-m4 -fno-emit-moduleinfo -c -fdata-sections start.d  
 
arm-none-eabi-gdc -mthumb -mcpu=cortex-m4 -fno-emit-moduleinfo -c -fdata-sections start.d  

Revision as of 12:12, 21 December 2013

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

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

For LDC compile with

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

For LDC, object.d must NOT exist, or it will try to import it automatically.

For GDC compile with

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

For GDC, object.d MUST exist, and it is imported automatically.

Linking

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 */
}

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