www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Need help making minimal bare metal ARM Cortex-M D program

reply "Mike" <none none.com> writes:
I am very new to D, but I finally got my toolchain compiled and 
working.  I'm using LDC.  I failed with GDC and eventually gave 
up.

I am trying to get an _extremely_ minimal bare metal ARM Cortex-M 
HelloWorld-type program (no phobos, no runtime, nothing but what 
I type myself) compiled and executed on my STM32F4-based 
hardware.  I know the toolchain is buggy for arm right now, but 
I'm hoping I can do something about that if I can just get 
started.

Here's the basic C code and linker script for my hardware. It 
doesn't actually print "hello world".  I intend to add that after 
I get the following code compiled and downloaded to my hardware.

/***************************
* start.c
****************************/
// defined in linker script
extern unsigned long _stack_end;

void handler_reset(void)
{
   //Print hello world using SWI
}

__attribute__ ((section(".interrupt_vector")))
void (* const table_interrupt_vector[])(void) =
{
   (void *) &_stack_end,
   handler_reset
};

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

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

SECTIONS
{
   .isr_vector :
   {
     . = ALIGN(4);
     KEEP(*(.isr_vector))
     . = ALIGN(4);
   } >FLASH

   .text :
   {
     . = ALIGN(4);
     KEEP(*(.interrupt_vector))
     *(.text)
     *(.text*)
     *(.rodata)
     *(.rodata*)
     . = ALIGN(4);
   } > flash	
}

Can anyone out them tell me if/how this can be accomplished in D?

Is there some syntax equivalent to  __attribute__ 
((section(".interrupt_vector")))?

Would the following give me a minimal reset_handler?

// compile with: ldc2 -c -nodefaultlib -noruntime
module reset_handler;

extern(C) __gshared void * _Dmodule_ref;
extern(C) void reset_handler()
{ }

I've seen some examples out on the web, but they all either use 
C, or are written specifically for an x86/x86_64 platform.  So 
any help you could provide would be great to help me get started.
Nov 24 2013
next sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Sunday, 24 November 2013 at 13:00:39 UTC, Mike wrote:
 Is there some syntax equivalent to  __attribute__ 
 ((section(".interrupt_vector")))?
There isn't right now, but it would be entirely feasible to implement this in an LDC-specific extension. Could you open an issue on our GitHub tracker? David
Nov 24 2013
parent reply "Mike" <none none.com> writes:
On Sunday, 24 November 2013 at 13:30:10 UTC, David Nadlinger 
wrote:
 On Sunday, 24 November 2013 at 13:00:39 UTC, Mike wrote:
 Is there some syntax equivalent to  __attribute__ 
 ((section(".interrupt_vector")))?
There isn't right now, but it would be entirely feasible to implement this in an LDC-specific extension. Could you open an issue on our GitHub tracker? David
https://github.com/ldc-developers/ldc/issues/547
Nov 24 2013
parent reply "Timo Sintonen" <t.sintonen luukku.com> writes:
On Sunday, 24 November 2013 at 14:10:54 UTC, Mike wrote:
 On Sunday, 24 November 2013 at 13:30:10 UTC, David Nadlinger 
 wrote:
 On Sunday, 24 November 2013 at 13:00:39 UTC, Mike wrote:
 Is there some syntax equivalent to  __attribute__ 
 ((section(".interrupt_vector")))?
There isn't right now, but it would be entirely feasible to implement this in an LDC-specific extension. Could you open an issue on our GitHub tracker? David
https://github.com/ldc-developers/ldc/issues/547
A full working example for gdc and cortex-m4 (stm32f4) is in my repo at https://bitbucket.org/timosi/minlibd I have used startup file from st and have no need to put interrupt vectors in d code.
Nov 24 2013
parent "Mike" <none none.com> writes:
On Sunday, 24 November 2013 at 20:00:56 UTC, Timo Sintonen wrote:
 A full working example for gdc and cortex-m4 (stm32f4) is in my 
 repo at
 https://bitbucket.org/timosi/minlibd

 I have used startup file from st and have no need to put 
 interrupt vectors in d code.
Thanks Timo, your minlibd is awesome, and will be my next object of study. I already know how do do what you did with assembly and C, but my goal is not just to get something working, but to learn what can and can't be done with D. minlibd uses an assembly startup file and calls into C functions to set up the clock, flash, etc... That works, and may be the only (best?) way to go. But I'd like to explore and learn what limitations exist in the D language and the D tools. If the LDC folks add a section-attribute-like feature, then I believe it should be possible to do away with the assembly startup file, and put the vectors, data section relocation, and bss initialization all in D, and that appeals to me. I'm wondering, though, if the vector table's simple name could be added to the text section. Then the attribute syntax would be unnecessary. Something like this: void (* const table_interrupt_vector[])(void) = { (void *) &_stack_end, handler_reset }; .text : { . = ALIGN(4); *(.text.table_interrupt_vector) *(.text) *(.text*) *(.rodata) *(.rodata*) . = ALIGN(4); } > flash ...but maybe the symbol will be added twice. Unfortunately I won't be home for a few more days to try it out. According to LDs docs, execution should default to the start of text, and if I could just figure out how to make sure table_interrrupt_vector is there, that would basically be it.
Nov 24 2013
prev sibling parent reply "Mike" <none none.com> writes:
I finally succeeded in doing what I set out to do:  Write a 
simple hello world program for an ARM Cortex-M processor using 
ONLY D.

/*************************
* The D Program (start.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);
   }
}

/*****************************
* The 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
{
   .text :
   {
     LONG(_stackStart);              /* Initial stack pointer */
     KEEP(start.o(.data.rel.ro))     /* 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 */
}

Tools used:
Operating System: Arch Linux 64-bit
Compiler:  LDC (2063b4)
Linker & Binary Utilities & Debugger: GNU Tools for ARM Embedded 
Processors (https://launchpad.net/gcc-arm-embedded)
JTAG Emulator: JTAG-lock-pick Tiny 2 w/ OpenOCD 0.7.0

To compile:
ldc2 -march=thumb -mcpu=cortex-m4 -noruntime -nodefaultlib -c 
start.d

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

To execute:
openocd -f interface/jtag-lock-pick_tiny_2.cfg -f 
target/stm32f4x.cfg
arm-none-eabi-gdb start.elf

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

Output:
hello
hello
...

Code Size: 148 bytes (not bad)

Why I think this is significant:
1.  It shows D can write the most low level of programs and does 
not require an operating system
2.  It shows that the D runtime and D standard library are not 
mandatory and do not need to be fully ported to one's platform to 
begin programming ARM Cortex-M bare metal hardware in D (although 
this is not the first to do so: 
https://bitbucket.org/timosi/minlibd)
3.  It shows linking to C code and assembly files are optional
4.  It shows the tools are capable (although they have not been 
well exercised in this example) and more specifically MY 
toolchain is working.
5.  It's a great start to writing very embedded systems in D, or 
more importantly, not C/C++ (good riddance!)

What's next for me:
1. Get a GDC toolchain working, although I'll probably switch to 
LDC when LDC matures.
2. Learn more about D.
3. Study minlibd and the d runtime and program the bare 
essentials to make D a productive language for the ARM Cortex-M.
4. Help the D community help me, by testing the toolchains for 
the ARM Cortex-M platform and file bug reports.

Thanks to those who commented on my previous posts. I'm quite 
excited about this language.
Nov 30 2013
next sibling parent Iain Buclaw <ibuclaw gdcproject.org> writes:
On Nov 30, 2013 11:40 AM, "Mike" <none none.com> wrote:
 I finally succeeded in doing what I set out to do:  Write a simple hello
world program for an ARM Cortex-M processor using ONLY D.
 /*************************
 * The D Program (start.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);
   }
 }

 /*****************************
 * The 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
 {
   .text :
   {
     LONG(_stackStart);              /* Initial stack pointer */
     KEEP(start.o(.data.rel.ro))     /* 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 */
 }

 Tools used:
 Operating System: Arch Linux 64-bit
 Compiler:  LDC (2063b4)
 Linker & Binary Utilities & Debugger: GNU Tools for ARM Embedded
Processors (https://launchpad.net/gcc-arm-embedded)
 JTAG Emulator: JTAG-lock-pick Tiny 2 w/ OpenOCD 0.7.0

 To compile:
 ldc2 -march=thumb -mcpu=cortex-m4 -noruntime -nodefaultlib -c start.d

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

 To execute:
 openocd -f interface/jtag-lock-pick_tiny_2.cfg -f target/stm32f4x.cfg
 arm-none-eabi-gdb start.elf

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

 Output:
 hello
 hello
 ...

 Code Size: 148 bytes (not bad)

 Why I think this is significant:
 1.  It shows D can write the most low level of programs and does not
require an operating system
 2.  It shows that the D runtime and D standard library are not mandatory
and do not need to be fully ported to one's platform to begin programming ARM Cortex-M bare metal hardware in D (although this is not the first to do so: https://bitbucket.org/timosi/minlibd)
 3.  It shows linking to C code and assembly files are optional
 4.  It shows the tools are capable (although they have not been well
exercised in this example) and more specifically MY toolchain is working.
 5.  It's a great start to writing very embedded systems in D, or more
importantly, not C/C++ (good riddance!)
 What's next for me:
 1. Get a GDC toolchain working, although I'll probably switch to LDC when
LDC matures.
 2. Learn more about D.
 3. Study minlibd and the d runtime and program the bare essentials to
make D a productive language for the ARM Cortex-M.
 4. Help the D community help me, by testing the toolchains for the ARM
Cortex-M platform and file bug reports.
 Thanks to those who commented on my previous posts. I'm quite excited
about this language. In before the "that's not D! That's some D, a bit of some extended inline assembly, and a custom linker script." Congrats though. :) Regards -- Iain Buclaw *(p < e ? p++ : p) = (c & 0x0f) + '0';
Nov 30 2013
prev sibling next sibling parent "Timo Sintonen" <t.sintonen luukku.com> writes:
On Saturday, 30 November 2013 at 11:37:25 UTC, Mike wrote:

 What's next for me:
 1. Get a GDC toolchain working, although I'll probably switch 
 to LDC when LDC matures.
 2. Learn more about D.
 3. Study minlibd and the d runtime and program the bare 
 essentials to make D a productive language for the ARM Cortex-M.
 4. Help the D community help me, by testing the toolchains for 
 the ARM Cortex-M platform and file bug reports.

 Thanks to those who commented on my previous posts. I'm quite 
 excited about this language.
Congratulations from me too. I just put my old gdc cross compiler instructions in the wiki: https://bitbucket.org/timosi/minlibd/wiki/gdc_cross_compiler If you are not going to have your own repository, feel free to send pull requests to me.
Nov 30 2013
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Saturday, 30 November 2013 at 11:37:25 UTC, Mike wrote:
 I finally succeeded in doing what I set out to do:  Write a 
 simple hello world program for an ARM Cortex-M processor using 
 ONLY D.
That's some nice progress indeed! Would you mind transferring your excellent description of the process to the D wiki and link it from http://wiki.dlang.org/LDC? This way, it would be easier to find in a few months than an old forum post. David
Dec 01 2013
parent reply "Mike" <none none.com> writes:
 Would you mind transferring your excellent description of the 
 process to the D wiki and link it from 
 http://wiki.dlang.org/LDC? This way, it would be easier to find 
 in a few months than an old forum post.

 David
http://wiki.dlang.org/Extremely_minimal_semihosted_%22Hello_World%22
Dec 02 2013
parent "David Nadlinger" <code klickverbot.at> writes:
On Monday, 2 December 2013 at 11:09:15 UTC, Mike wrote:
 http://wiki.dlang.org/Extremely_minimal_semihosted_%22Hello_World%22
Thanks a lot, Mike! I linked the page from the LDC platform overview. David
Dec 02 2013