D on AVR

From D Wiki
Revision as of 22:55, 20 December 2019 by ErnyTech (talk | contribs) (Fix build)
Jump to: navigation, search

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.

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
  $ 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 and 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