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

From D Wiki
Jump to: navigation, search
(The Code (GDC))
(The Code (LDC))
Line 15: Line 15:
 
* (JTAG emulator) [http://www.distortec.com/jtag-lock-pick-tiny-2 JTAG-lock-pick Tiny 2]
 
* (JTAG emulator) [http://www.distortec.com/jtag-lock-pick-tiny-2 JTAG-lock-pick Tiny 2]
  
=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)
 
+
{
 +
  import ldc.llvmasm;
 +
}
 +
 
extern(C) __gshared void * _Dmodule_ref;
 
extern(C) __gshared void * _Dmodule_ref;
 
+
 
//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";
 +
    };
 +
  }
 
}
 
}
 
+
 
void OnReset()
 
void OnReset()
 
{
 
{

Revision as of 11:33, 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 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);
  }
}

Compile with:

ldc2 -march=thumb -mcpu=cortex-m4 -c 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)

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

Link with:

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


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

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