Difference between revisions of "Minimal semihosted ARM Cortex-M "Hello World""
(→Compiling) |
m (→Program Source Code (start.d)) |
||
(26 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
− | The following is | + | The following is a minimal, semihosted "Hello World" D program for ARM Cortex-M processors. |
=Goals= | =Goals= | ||
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 |
− | * ( | + | * (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. | + | * (GDB server) [http://openocd.sourceforge.net/ OpenOCD] 0.9.0 |
− | * ( | + | * ([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= | =The Code= | ||
Line 24: | Line 24: | ||
import ldc.llvmasm; | 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 | ||
Line 53: | Line 51: | ||
: | : | ||
: [cmd] "r" command, [msg] "r" message | : [cmd] "r" command, [msg] "r" message | ||
− | : "r0", "r1"; | + | : "r0", "r1", "memory"; |
− | } | + | } |
} | } | ||
} | } | ||
Line 62: | Line 60: | ||
while(true) | while(true) | ||
{ | { | ||
− | // Create semihosting | + | // Create semihosting message |
uint[3] message = | uint[3] message = | ||
[ | [ | ||
Line 77: | Line 75: | ||
==Minimal Runtime Implementation (object.d) (GDC only)== | ==Minimal Runtime Implementation (object.d) (GDC only)== | ||
− | GDC requires the following minimal object.d file in the | + | 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. |
<syntaxhighlight lang="D"> | <syntaxhighlight lang="D"> | ||
module object; | module object; | ||
− | |||
− | |||
</syntaxhighlight > | </syntaxhighlight > | ||
Line 87: | Line 83: | ||
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...) | ||
− | === | + | ===LDC=== |
<source lang="html4strict"> | <source lang="html4strict"> | ||
− | ldc2 - | + | 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. | For LDC, object.d must NOT exist, or it will try to import it automatically. | ||
− | === | + | ===GDC=== |
<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 | ||
</source> | </source> | ||
− | For GDC, object.d MUST exist, and it is imported automatically. | + | For GDC, object.d MUST exist in the compiler's import path, and it is imported automatically. |
=Linking= | =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. | |
− | NOTE: This linker | + | ==Linker Script (link.ld) == |
<source lang="html4strict"> | <source lang="html4strict"> | ||
MEMORY | MEMORY | ||
Line 113: | Line 109: | ||
SECTIONS | SECTIONS | ||
− | { | + | { |
− | + | /* We don't need exceptions, and discarding these sections | |
+ | prevents linker errors with LDC */ | ||
+ | /DISCARD/ : | ||
{ | { | ||
− | + | *(.ARM.extab*) | |
− | + | *(.ARM.exidx*) | |
− | + | } | |
− | |||
− | *(. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
.text : | .text : | ||
{ | { | ||
− | LONG(_stackStart); | + | LONG(_stackStart); /* Initial stack pointer */ |
− | KEEP(start.o( | + | KEEP(start.o(*.ResetHandler)) /* Interrupt vector table (Entry point) */ |
/* the code */ | /* the code */ | ||
Line 184: | Line 148: | ||
Start OpenOCD: | Start OpenOCD: | ||
<source lang="html4strict"> | <source lang="html4strict"> | ||
− | openocd -f | + | openocd -f board/stm32f429discovery.cfg |
</source> | </source> | ||
Line 209: | Line 173: | ||
</source> | </source> | ||
− | + | [[Category:ARM]] | |
− | |||
− | |||
− | |||
− | |||
− | [[Category: |
Latest revision as of 15:06, 18 December 2017
The following is a minimal, semihosted "Hello World" D program for ARM Cortex-M processors.
Contents
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
- (host computer) Arch Linux 64-bit
- (compiler) LDC with ARM backend, or GDC cross-compiler for arm-none-eabi 4.9.2
- (GDB client) GNU Tools for GNU Tools for ARM Embedded Processors
- (GDB server) OpenOCD 0.9.0
- (In Circuit Emulator) STLink v2 on STM32F429I-DISCO board
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
...