LightWeight D Runtime targeting ARM Cortex CPUs
This is not a port of druntime! This is a completely new implementation for low-resource environments. Normal D code may not work here!
LWDR is now part of the Symmetry Autumn of Code 2021. The work plan is here.
This is the light weight D runtime - it is a barebones runtime targeting ARM Cortex CPUs. It works by abstracting hooks that the user can connect to their selected backend (be it an RTOS such as FreeRTOS, ChibiOS, etc or a minimalist system).
new
and delete
)new
and delete
)TypeInfo
stubs)LWDR_DynamicArray
)LWDR_DynamicArray
)LWDR_DynamicArray
)LWDR_DynamicArray
)LWDR_TLS
)LWDR_ManualDelegate
)LWDR_ModuleCtors
)LWDR_ModuleCtors
)LWDR_ModuleCtors
)LWDR_ModuleCtors
)LWDR_Sync
)LWDR_Sync
)LWDR_TrackMem
, RefCount!T
and Unique!T
are now available)LDC works the best. DMD is not compatible. GDC will work but points 18-21 inclusive aren't supported.
Yes, as of currently it has been run on an STM32F407.
You have to hook the functions declared in rtoslink.d
by implementing them in your MCU development environment. For example, with FreeRTOS, rtosbackend_heapalloc
points to a wrapper in the C/C++ land that wraps pvPortMalloc(...)
.
First off, you will need an existing C/C++ project for your target microcontroller that has a C compiler and link, and has some form of memory management (RTOS preferred). The C/C++ code can then call into your D functions (they must be marked extern(C)
).
LWDR can be used with DUB and LDC. Simply add it to your dependencies. Build instructions are here for DUB and LDC.
This will output a lib archive that you can link into your C/C++ project and execute on an MCU.
Here is an example code using FreeRTOS:
//myapp.d
module myapp;
import lwdr;
class Foo
{
this()
{
// do something
}
void bar()
{
// do something
}
}
extern(C) void myDFunction()
{
Foo foo = new Foo; // this will invoke rtosbackend_heapalloc(..)
foo.bar;
LWDR.free(foo); // don't forget to free - there is no GC
// LWDR.free will invoke rtosbackend_heapfreealloc(..)
}
// main.h
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
// defined in rtoslink.d
void* rtosbackend_heapalloc(unsigned int sz);
void rtosbackend_heapfreealloc(void* ptr);
void rtosbackend_arrayBoundFailure(char* file, unsigned int line);
void rtosbackend_assert(char* file, unsigned int line);
void rtosbackend_assertmsg(char* msg, char* file, unsigned int line);
void myDFunction(); // defined in myapp.d
#ifdef __cplusplus
}
#endif
#endif __MAIN_H
// main.cpp
#include "cmsis_os.h"
void* rtosbackend_heapalloc(unsigned int sz) // defined in rtoslink.d
{
return pvPortMalloc(sz); // allocate some heap memory for D
}
void rtosbackend_heapfreealloc(void* ptr)// defined in rtoslink.d
{
vPortFree(ptr); // deallocate some heap memory for D
}
void rtosbackend_arrayBoundFailure(char* file, unsigned int line)
{}
void rtosbackend_assert(char* file, unsigned int line)
{}
void rtosbackend_assertmsg(char* msg, char* file, unsigned int line)
{}
osThreadId_t defaultTaskHandle; // thread handle
osThreadAttr_t defaultTask_attributes; // thread attributes
void myTask(void *argument)
{
myDFunction();
}
int main()
{
osKernelInitialize();
defaultTask_attributes.name = "defaultTask";
defaultTask_attributes.priority = (osPriority_t) osPriorityNormal;
defaultTask_attributes .stack_size = 128 * 4;
// create a thread that executes myTask
defaultTaskHandle = osThreadNew(myTask, NULL, &defaultTask_attributes);
osKernelStart(); // start the scheduler
while(1) {}
return 1;
}
GDB will be able to set breakpoints in D code and perform steps normally.
Credit to Adam D. Ruppe for his webassembly project that forms the basis of this project.
Credit to D Language Foundation for its D runtime.
Credit to LDC Developers for its D runtime.
Credit to GDC for its D runtime.
Credit to denizzka for his d_c_arm_test which helped with the implementation of TLS (thread local storage).