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

From D Wiki
Jump to: navigation, search
m (Program Source Code (start.d))
 
(40 intermediate revisions by 3 users not shown)
Line 1: Line 1:
The following is an extremely minimal, semihosted "Hello World" D program for ARM Cortex-M processors.
+
The following is a minimal, semihosted "Hello World" D program for ARM Cortex-M processors.
  
 
=Goals=   
 
=Goals=   
 
* Verify the ARM Cortex-M toolchain (compiler-->linker-->debugger)
 
* Verify the ARM Cortex-M toolchain (compiler-->linker-->debugger)
* Demonstrate that the D runtime and D standard library are not mandatory
+
* 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 linking to C code is not necessary
 
* Demonstrate that all required assembly code can be done within D
 
* Demonstrate that all required assembly code can be done within D
Line 10: Line 10:
 
=Tools=
 
=Tools=
 
* (host computer) [https://www.archlinux.org/ Arch Linux] 64-bit
 
* (host computer) [https://www.archlinux.org/ Arch Linux] 64-bit
* (compiler) [http://wiki.dlang.org/LDC LDC] with ARM backend
+
* (compiler) [http://wiki.dlang.org/LDC LDC] with ARM backend, or [http://wiki.dlang.org/GDC/Cross_Compiler GDC cross-compiler] for arm-none-eabi 4.9.2
* (linker & debugger) GNU Tools for [https://launchpad.net/gcc-arm-embedded GNU Tools for ARM Embedded Processors] 4.7-2013-q3
+
* (GDB client) GNU Tools for [https://launchpad.net/gcc-arm-embedded GNU Tools for ARM Embedded Processors]
* (GDB server) [http://openocd.sourceforge.net/ OpenOCD] 0.7.0
+
* (GDB server) [http://openocd.sourceforge.net/ OpenOCD] 0.9.0
* (JTAG emulator) [http://www.distortec.com/jtag-lock-pick-tiny-2 JTAG-lock-pick Tiny 2]
+
* ([http://en.wikipedia.org/wiki/In-circuit_emulator In Circuit Emulator]) STLink v2 on [http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090 STM32F429I-DISCO board]
  
=The Code (LDC)=
+
=The Code=
 
==Program Source Code (start.d)==
 
==Program Source Code (start.d)==
 
<syntaxhighlight lang="D">
 
<syntaxhighlight lang="D">
 
module start;
 
module start;
 
+
import ldc.llvmasm;
+
version(LDC)
 
+
{
extern(C) __gshared void * _Dmodule_ref;
+
  import ldc.llvmasm;
 
+
}
 +
 
//Must be stored as second 32-bit word in .text section
 
//Must be stored as second 32-bit word in .text section
immutable void function() ResetHandler = &OnReset;
+
alias void function() ISR;
 
+
extern(C) immutable ISR ResetHandler = &OnReset;
 +
 
void SendCommand(int command, void* message)
 
void SendCommand(int command, void* message)
 
{
 
{
   __asm
+
   version(LDC)
  (
+
  {
    "mov r0, $0;
+
    __asm
    mov r1, $1;
+
    (
    bkpt #0xAB",
+
      "mov r0, $0;
    "r,r,~{r0},~{r1}",
+
      mov r1, $1;
    command, message
+
      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", "memory";
 +
    }
 +
  }
 
}
 
}
 
+
 
void OnReset()
 
void OnReset()
 
{
 
{
 
   while(true)
 
   while(true)
 
   {
 
   {
     // Create semihosting message message
+
     // Create semihosting message
 
     uint[3] message =
 
     uint[3] message =
 
       [
 
       [
Line 57: Line 74:
 
</syntaxhighlight >
 
</syntaxhighlight >
  
Compile with:
+
==Minimal Runtime Implementation (object.d) (GDC only)==
<source lang="html4strict">
+
GDC requires the following minimal object.d file in compiler's import path, which by default is the compiler's working directory.  If compiling from within folder that contains start.d, simply add object.d to that folder.  It is imported automatically.
ldc2 -march=thumb -mcpu=cortex-m4 -c start.d
+
<syntaxhighlight lang="D">
</source>
+
module object;
You will have to change the -mcpu option for your processor accordingly (e.g. cortex-m0, cortex-m3, etc...)
+
</syntaxhighlight >
  
==Linker Script (link.ld)==
+
=Compiling=
NOTE: This linker script was written specifically for an STM32F4 MCU.  The ''MEMORY'' section and ''_stackStart'' will have to be tailored to your hardware.
+
NOTE: You will have to change the -mcpu option for your processor accordingly (e.g. cortex-m0, cortex-m3, etc...)
<source lang="html4strict">
 
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 */
 
}
 
</source>
 
  
Link with:
+
===LDC===
 
<source lang="html4strict">
 
<source lang="html4strict">
arm-none-eabi-ld -T link.ld --gc-sections start.o -o start.elf
+
ldc2 -mtriple=thumb-none-linux-eabi -mcpu=cortex-m4 -c -betterC start.d
 
</source>
 
</source>
 +
For LDC, object.d must NOT exist, or it will try to import it automatically.
  
=The Code (GDC)=
+
===GDC===
==Program Source Code (start.d)==
 
<syntaxhighlight lang="D">
 
module start;
 
 
 
import ldc.llvmasm;
 
 
 
extern(C) __gshared void * _Dmodule_ref;
 
 
 
//Must be stored as second 32-bit word in .text section
 
immutable void function() ResetHandler = &OnReset;
 
 
 
void SendCommand(int command, void* message)
 
{
 
  __asm
 
  (
 
    "mov r0, $0;
 
    mov r1, $1;
 
    bkpt #0xAB",
 
    "r,r,~{r0},~{r1}",
 
    command, message
 
  );
 
}
 
 
 
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);
 
  }
 
}
 
</syntaxhighlight >
 
 
 
Compile with:
 
 
<source lang="html4strict">
 
<source lang="html4strict">
ldc2 -march=thumb -mcpu=cortex-m4 -c start.d
+
arm-none-eabi-gdc -mthumb -mcpu=cortex-m4 -fno-emit-moduleinfo -c -fdata-sections start.d  
 
</source>
 
</source>
You will have to change the -mcpu option for your processor accordingly (e.g. cortex-m0, cortex-m3, etc...)
+
For GDC, object.d MUST exist in the compiler's import path, and it is imported automatically.
  
==Linker Script (link.ld)==
+
=Linking=
NOTE: This linker script was written specifically for an STM32F4 MCU.  The ''MEMORY'' section and ''_stackStart'' will have to be tailored to your hardware.
+
NOTE: This linker scripts were written specifically for an STM32F4 MCU.  The ''MEMORY'' section and ''_stackStart'' will have to be tailored to your hardware.
 +
==Linker Script (link.ld) ==
 
<source lang="html4strict">
 
<source lang="html4strict">
 
MEMORY
 
MEMORY
Line 163: Line 109:
  
 
SECTIONS
 
SECTIONS
{
+
{
   .text :
+
  /* We don't need exceptions, and discarding these sections
 +
    prevents linker errors with LDC */
 +
  /DISCARD/ :
 +
  {
 +
    *(.ARM.extab*)
 +
    *(.ARM.exidx*)
 +
  }
 +
 
 +
   .text :      
 
   {
 
   {
 
     LONG(_stackStart);              /* Initial stack pointer */
 
     LONG(_stackStart);              /* Initial stack pointer */
     KEEP(start.o(.data.rel.ro))     /* Interrupt vector table */
+
     KEEP(start.o(*.ResetHandler))   /* Interrupt vector table (Entry point) */
 
+
   
 
     /* the code */
 
     /* the code */
     *(.text)
+
     *(.text)    
     *(.text*)
+
     *(.text*)
 
+
   
 
     /* for "hello\r\n" string constant */
 
     /* for "hello\r\n" string constant */
 
     . = ALIGN(4);
 
     . = ALIGN(4);
 
     *(.rodata)
 
     *(.rodata)
 
     *(.rodata*)
 
     *(.rodata*)
 +
   
 
   }>FLASH
 
   }>FLASH
 
+
 
 +
 
 
   /* Need .data, .bss, .ctors and probably more as program becomes
 
   /* Need .data, .bss, .ctors and probably more as program becomes
 
     More complex */
 
     More complex */
Line 188: Line 144:
 
arm-none-eabi-ld -T link.ld --gc-sections start.o -o start.elf
 
arm-none-eabi-ld -T link.ld --gc-sections start.o -o start.elf
 
</source>
 
</source>
 
  
 
=Execution=
 
=Execution=
 
Start OpenOCD:
 
Start OpenOCD:
 
<source lang="html4strict">
 
<source lang="html4strict">
openocd -f interface/jtag-lock-pick_tiny_2.cfg -f target/stm32f4x.cfg
+
openocd -f board/stm32f429discovery.cfg
 
</source>
 
</source>
  
Line 218: Line 173:
 
</source>
 
</source>
  
=TODO=
+
[[Category:ARM]]
* 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
 
 
 
[[Category:LDC]]
 

Latest revision as of 15:06, 18 December 2017

The following is a 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;
}
 
//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", "memory";
    }
  }
}
 
void OnReset()
{
  while(true)
  {
    // Create semihosting 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 compiler's import path, which by default is the compiler's working directory. If compiling from within folder that contains start.d, simply add object.d to that folder. It is imported automatically.

module object;

Compiling

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

LDC

ldc2 -mtriple=thumb-none-linux-eabi -mcpu=cortex-m4 -c -betterC start.d

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

GDC

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

For GDC, object.d MUST exist in the compiler's import path, and it is imported automatically.

Linking

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

Linker Script (link.ld)

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
{  
  /* We don't need exceptions, and discarding these sections
     prevents linker errors with LDC */
  /DISCARD/ :
  {
    *(.ARM.extab*)
    *(.ARM.exidx*)
  }

  .text :       
  {
    LONG(_stackStart);              /* Initial stack pointer */
    KEEP(start.o(*.ResetHandler))   /* Interrupt vector table (Entry point) */
    
    /* 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 board/stm32f429discovery.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
...