D on AVR
AVR is a family of 8-bit RISC microcontrollers, are widely used for embedded applications where a simple, inexpensive and low-powered microcontroller is needed.
In the FOSS community, AVR-GCC has always been used as a toolchian for AVR which includes all the tools needed to compile a C source, copy the object files into a hex file and write it to the ROM of the microcontroller.
The merge of the AVR backend for LLVM (https://github.com/avr-llvm) in the upstream LLVM project has given rise to the porting of modern languages to develop on AVR, such as Rust (https://github.com/avr-rust), Go (https://github.com/tinygo-org/tinygo) and Swift (http://swiftforarduino.com/).
D has its frontend for LLVM so you can use it to program on AVR.
Contents
Get LLVM source
$ wget https://releases.llvm.org/9.0.0/llvm-9.0.0.src.tar.xz
$ tar xf llvm-9.0.0.src.tar.xz
Build and install LLVM with AVR target
$ cd llvm-9.0.0.src
$ mkdir build
$ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=AVR \
-DLLVM_TARGETS_TO_BUILD= -DLLVM_BINUTILS_INCDIR=/usr/include \
-D CMAKE_INSTALL_PREFIX=/opt/llvm-avr
$ sudo ninja install
Get LDC source
$ git clone https://github.com/ldc-developers/ldc -b v1.18.0
Build and install ldc with LLVM AVR
$ cd ldc
$ mkdir build
$ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/opt/ldc-avr -DLLVM_ROOT_DIR=/opt/llvm-avr
$ ninja ldc2
$ sudo ninja install
Test D with AVR
enum AVR_ARCH = 5; // AVR architecture of your MCU
static if (AVR_ARCH >= 100) {
enum SFR_OFFSET = 0x00;
} else {
enum SFR_OFFSET = 0x20;
}
enum ubyte* MMIO_BYTE(ubyte memAddr) = cast(ubyte*) memAddr;
enum ubyte* SFR_IO8(ubyte ioAddr) = MMIO_BYTE!(ioAddr + SFR_OFFSET);
enum ubyte* PINB = SFR_IO8!(0x03);
enum ubyte* DDRB = SFR_IO8!(0x04);
enum ubyte* PORTB = SFR_IO8!(0x05);
extern(C) void main() {
import core.bitop;
volatileStore(DDRB, 0xFF); // Set all PORT B to output
while (true) {
volatileStore(PORTB, 0xFF); // Set all PORT B to high
}
}
This simple code will set all I/O PORTS B to high, on Arduino UNO for example the PORT B is connected to the digital pins from 8 to 13, on Arduino UNO this code will cause the internal LED to light up as it is connected to PIN 13.
How to build and run the test code
First of all to generate an object file for AVR, convert it to HEX and then flash it into the flash memory of the MCU is advisable to use AVR GCC as C toolchain, linker, objcopy (to convert to hex) and avrdude (to load it into flash).
Therefore it is necessary to install AVR-GCC, to do this consult the documentation of your operating system.
The following commands need AVR-GCC installed and in a directory present in the PATH environment variable and are meant to be used with Arduino UNO, minor modifications are needed for other AVR MCUs
$ /opt/ldc-avr/bin/ldc2 -betterC -Oz -mtriple=avr -mcpu=atmega328p -Xcc=-mmcu=atmega328p -gcc=avr-gcc test.d
$ avr-objcopy -O ihex -R .eeprom test test.hex // -R .eeprom is used to avoid overwriting the EEPROM, since is used to store data
$ avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:test.hex // Write the hex in the ROM and reset MCU
Current projects for use D on AVR
Info about the GNU/LINUX distros
- ArchLinux, The llvm package in the official repo is already builded with the AVR flag (https://git.archlinux.org/svntogit/packages.git/tree/trunk/PKGBUILD?h=packages/llvm#n40), therefore the ArchLinux LDC package also supports the AVR target out-of-the-box