You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Also, although it is not required, I use just as the command runner, just like the reference source. Make is fine, but I felt like I could understand the FAQ's philosophy, so I tried using it.
UEFI target
This time I posted everything to ldc2.conf. Switches are the best when it comes to cross-compiling.
In the original version, there are various references to OS memory allocation and libc functions, but here we use things -betterC like to --defaultlib='' to eliminate dependence on the runtime, so this is not a particular problem.
UEFI bootloader
The UEFI-related definitions are as follows. There's not much to see here extern(C). The only difference is that it is defined at the top level.
Being able to write naked function and use inline assembly is an essential requirement for a system programming language.
output String requires a null-terminated UTF-16 string, but the D language has a built-in UTF-16 string type wstring, so this is easy \0. Be careful to include at the end wchar* and cast to to use it.
D language application method on microcontroller (ARM Cortex-M)
Goals
Try to use Dlang language to write a microcontroller program. The target chip is STM32f401cc. The cortex-m4
HOST host is a windows system.
List of advantages
D language is a programming language without "language religion", similar to the design of C/C++. There are no order restrictions on function writing, no non-debuggable definitions, and no complex header files.
No need to build a complex cross-compilation environment when using the LDC compiler
Programming on a microcontroller mainly uses a subset of the D language BetterC. This subset includes the implementation of most of the D language. The following functions are not implemented:
Serial Number
Function
Comment
1
Memory GC
In terms of microcontroller programming, this part still requires programmers to consider trade-offs.
2
structural information
TypeInfo and ModuleInfo , the main application of this part is GC, and there are some shared library applications
3
Class
---
4
Built-in threads
---
5
dynamic array
---
6
Exceptions
---
7
Built-in synchronization function
---
8
Static constructors and destructors
---
Of course, you can also use the complete D language, but the cost is higher (FLASH/RAM). Generally speaking, the running state of the program developed by the microcontroller can be inferred. From an efficiency perspective, it is not recommended to use GC management or dynamic loading.
Embedded development of Linux will not be discussed in this article.
Environmental preparation
First we determine what we need:
Serial Number
Function
Currently available
1
IDE
Vscode is recommended, plus the webfreak.dlang-bundle plug-in and marus25.cortex-debug debugging plug- in used by dlang
2
translater
LDC, implemented through LLVM, can directly download the binary version without the need for RT libraries.
2.1
Connector
lld, it is recommended to use the LDC built-in directly
2.2
Miscellaneous tools
LLVM, including many tools that you are not used to, can also be used with arm-none-eabi
3
Build tools
This is not available for the time being. Let’s write a batch process to get it done 😀
4
debugger
GDB, in theory it can support the gdb protocol, but I haven’t found a better solution yet.
5
Simulation
qemu-arm, this simulator can simulate multiple series of stm32. There is a problem with the floating point part of the hardware, and errors will occur during simulation.
Let’s talk about some limitations in the code. These limitations are mainly caused by the implementation of TLS and the fact that this part of the function is not implemented in the library that comes with ldc. The relationship between D language function
declaration and C between D language function declaration and C++ Relationship
The trouble encountered here mainly comes from the TLS part, which lacks implementation functions and bugs
in the LLD part of LLVM (the binary file output after using TLS is very huge ) .
It is recommended not to use ABI's TLS and use emulated-tls instead.
However, there is no need for TLS on a " streaking " microcontroller. If you are using an embedded system,
it is recommended to implement the emulated-tls method.
Linker script
/*
filename: ldscript.ld
*/
MEMORY
{
FLASH (RX): ORIGIN = 0X08000000, LENGTH = 256K
SRAM (WXAR): ORIGIN = 0X020000000, LENGTH = 64K
}
_stackStart = ORIGIN(CCRAM) + LENGTH(CCRAM);
SECTION
{
/*
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 (entrypoint)
// text code
*(.text)
*(.text*)
// for "hello\r\n" string
. = ALIGN(4);
*(.rodata)
*(.rodata*)
}>FLASH
/*
Need .data, .bss, .ctors and probably more as program becomes
More complex
*/
}
This connection script does not implement SECTIONS for memory operations, it is just for this demonstration!!!
I will post a linker script with comments later.
If you want to get more debugging information, add the ldc compilation parameters and --gcuse elf as the output format to get more debugging information.
Rest of instructions
You can use the debugging function of vscode to debug, and a documentation will be written later.
This time, qemu_cortex_m3my first goal was to move it.
Therefore, the LDC target thumbv7em-none-linux-musl-gnueabi is set.
I also wanted to use newlib functions, so CONFIG_NEWLIB_LIBC=y I defined them in prj.conf.
Why is musl specified in target triple even though newlib is used? That part will be explained later.
All that's left to do is specify it, define it as an ExternalProject from CMake, call it, arrange it as you like, and it should work.
$ cd$ZEPHYR_BASE
$ west build -b qemu_cortex_m3 samples/ldc_hello
$ west build -t run
(...)
Hello from 'LDC'!
assertion "array index out of bounds" failed: file "d_src/hello.d", line 29, function: hello.d_main
exit
The D language compiler supports array bounds checking.
At that time, you can select the behavior when an out-of-bounds access occurs with the --checkaction option from halt/C/D/context, but if you select here, if an out-of-bounds access occurs, the C
language --checkaction=C The process is to call the assert function.
However, rather than calling the assert function directly, it calls a function equivalent to assert, which has a different definition for each platform architecture.
In terms of the compiler, the following is an example of this processing.
So this time, I set the target to Musl, __assert_fail defined a function, __assert_func wrapped the newlib function,
and modified it so that the arguments corresponded as expected.
The reason for using musl here is that it __assert is considered to have the least environmental impact among those that can avoid overloading. The compiler tries to call
musl target, but newlib naturally doesn't have such a thing, so it calls the one you defined yourself.__assert_fail
Another references