-- Leo's gemini proxy

-- Connecting to gem.snowgoons.ro:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

gem.snowgoons.ro

Story so far


In November last year[1], I wrote about my early efforts to get Rust to compile on the Arduino Nano Every[2] - the latest, and cheapest/most convenient as an embedded controller - iteration of the Arduino family of Microchip/Atmel AVR 8-bit microcontroller based boards.


The ATmega4809[3] chip that the Every is based on is in many ways a super nice chip, small, fast, power light, and with more memory for your code and data than chips used in older Arduino boards. Unfortunately, it's also based on a somewhat different internal architecture - `avrxmega3` - than those other boards, meaning that toolchain support is best described as "broken to non existent". The `avr-gcc` you installed from a package probably can't even compile or link properly for it, before you even begin to think about Rust[4]'s limited support for AVR.


What's New


As evidenced by my last post on this subject being in November 2020, progress is not exactly quick. That's mostly a factor of the day job presenting many other challenges in the meantime than the complexity of the task though.


I am slowly making some progress on getting useful code to build and run, though. Hopefully some of the things I've discovered will be helpful to anyone else taking the same journey...


Dependencies


First off, toolchain dependencies. In my last post, I had managed to get a basic LED-blinking program to run using a hacked version of the Ruduino[5] crate. That was mostly by luck rather than judgement - but it did at least prove that it was possible.


But, since then, I've realised the first thing we need to do with this board is give up on 'standard' versions of the underlying compiler/linked toolchain, `avr-gcc` and `avr-ld`. They don't understand this chip properly... Fortunately, Microchip[6] maintain their own fork of `gcc` which *does* work properly with this chip.


So, my first advice to you is to download and install the Atmel toolchain for your platform from the link below. After installing, modify your `PATH` so the Atmel version of the commands appears before any other version of `avr-gcc` you may have installed. (I am succesfully using both the Mac (on my desktop) and Linux (in my CI/CD pipeline) versions of these.)


ATpacks


Atmel also distribute "ATpacks" for each of their microchips. These contain a machine-readable description of the chip's capabilities and also some support libraries you need to link into your eventual binary, specific to each chip. You will need to download these too, and then you can point `avr-gcc` to the correct ATpack for the ATmega4809 using the `-B` command line flag. We'll see how to do this using Rust in the A Working Build Target section of this post.


┌────────────────────┬─────────────────────────────────────────────────────────┐
│      Resource      │                          Where                          │
╞════════════════════╪═════════════════════════════════════════════════════════╡
│ ATmega toolchain   │ https://www.microchip.com/en-us/development-tools-      │
│                    │ tools-and-software/gcc-compilers-avr-and-arm[7]         │
├────────────────────┼─────────────────────────────────────────────────────────┤
│ The ATmega ATpacks │ http://packs.download.atmel.com[8]                      │
└────────────────────┴─────────────────────────────────────────────────────────┘

Rust avr-hal crates


Last time round, I used the Ruduino[9] crate, which was a great start and I'm very grateful for it. But, I have discovered that there is a somewhat more recently maintained, and cleaner, HAL crate avr-hal[10], which it was a little easier to hack 4809 support into.


Note that there *will* be official support for the ATmega4809[11] in avr-hal[12] eventually - and it'll be a great day when it comes :). But at the moment there is a lot of good work happening in that project on more general development and refactoring of the HAL, so in the meantime I am using a slightly older version of the crate which I have *hacked* to provide basic support for the 4809.


Because this is a hack, and eventually will be deprecated in favour of the 'official' suppoort when it arrives, I am not publishing it on the official `crates.io` Rust crate repo. However, I *am* publishing it publicly for anyone who does wish to use it on a public Cloudsmith[13] Rust repo.


To use my packaged versions, add the following to the `.cargo/config.toml` file in your Rust project:


[registries]
snowgoons-crates = { index = "https://dl.cloudsmith.io/public/snowgoons/crates/cargo/index.git" }

You can then reference my 4809-compatible versions of the HAL for the Arduino Nano Every in your `Cargo.toml` like so:


arduino-nano-every = { version = "0.1.0", registry = "snowgoons-crates" }
avr-hal-generic = { version = "0.1.0", registry = "snowgoons-crates" }

The source is of course there in GitHub as well: | Crate | Github | | ----- | ------ | | arduino-nano-every, avr-hal-generic | https://github.com/timwalls/avr-hal/tree/arduino-nano-every[14] | | avr-device | https://github.com/timwalls/avr-device/tree/ATmega4809[15] |


A working build target


OK, so, now we have a working toolchain, and a HAL that is known to work, we need to give Rust a build target description that tells it to use the right toolchain for the ATmega4809. We do this using a JSON file in the root of our Cargo project.


Last time round, you may remember we used an `atmega328p` build target; this worked to get basic code working (the instruction set is the same,) but would fail as soon as you tried anything more ambitious because of architecture and memory map differences between the chips. Now we have a basically functioning working description that's actually right for the ATmega4809[16].


To use this description, add this to your `.cargo/config.toml`:


[build]
target = "avr-atmega4809.json"

Now you need to create the `avr-atmega4809.json` file like so:


{
  "arch": "avr",
  "atomic-cas": false,
  "cpu": "avrxmega3",
  "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
  "eh-frame-header": false,
  "env": "",
  "exe-suffix": ".elf",
  "executables": true,
  "late-link-args": {
    "gcc": [
      "-lgcc"
    ]
  },
  "linker": "avr-gcc",
  "linker-flavor": "gcc",
  "linker-is-gnu": true,
  "llvm-target": "avr-unknown-unknown",
  "max-atomic-width": 8,
  "no-default-libraries": false,
  "os": "unknown",
  "pre-link-args": {
    "gcc": [
      "-mmcu=atmega4809",
      "-Wl,--as-needed",
      "-Wl,-Map=target/memory.map",
      "-L","./atmel-atpack/atmega4809/avrxmega3",
      "-B","./atmel-atpack/atmega4809/"
    ]
  },
  "target-c-int-width": "16",
  "target-endian": "little",
  "target-pointer-width": "16",
  "vendor": "unknown"
}

There are a couple of important things to note:


If you're not using the ATmega toolchain, this will likely not work properly; depending on your OS/distribution, the `avr-gcc` you get from a package repo probably doesn't understand the `avrxmega3` architecture

The `"-L","./atmel-atpack/atmega4809/avrxmega3"` and `"-B","./atmel-atpack/atmega4809/"` lines need to point to the correct (atmega4809) directory in the ATpacks that you downloaded from Microchip. I copy these folders into my build environment/repo to guarantee consistent compilation across environments, but you could also just point to wherever you unzipped the ATpacks.


Deploying automatically with `cargo run`


Last time round I gave you a script[17] to upload compiled ELF code to your Arduino. This still works; the only thing to add here is that by adding a couple of lines to your `.cargo/config.toml` you can have Cargo automatically use this script when you use `cargo run`, which is a nice convenience:


[target.'cfg(target_arch = "avr")']
runner = "bin/arduino-nano-every-upload.sh"

Bringing it all together


If you got everything right, you should be able to test it's working with a test programme that looks something like this:


#![no_std]
#![no_main]

use arduino_nano_every::prelude::*;

#[arduino_nano_every::entry]

fn main() -> ! {
  let dp = arduino_nano_every::Peripherals::take().unwrap();

  let mut pins = arduino_nano_every::Pins::new(dp.PORTA, dp.PORTB, dp.PORTC, dp.PORTD, dp.PORTE, dp.PORTF);

  // On the Nano Every, the LED is on pin D13
  // Note - these are the *Arduino* pin references, not ATmega port references
  // it's the avr_hal:arduino_nano_every crate's job to map the Arduino pin refs
  // to the actual port used on the ATmega4809.
  let mut led = pins.d13.into_output(&mut pins.ddr);

  loop {
    led.toggle().void_unwrap();
    arduino_nano_every::delay_ms(500);
  }
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
  loop {}
}

The `Cargo.toml` would look like:


[package]
name = "blinky"
version = "0.1.0"
edition = "2018"

[dependencies]
arduino-nano-every = { version = "0.1.0", registry = "snowgoons-crates" }
avr-hal-generic = { version = "0.1.0", registry = "snowgoons-crates" }

[profile.dev]
panic = "abort"
lto = true
opt-level = "s"

[profile.release]
panic = "abort"
codegen-units = 1
debug = true
lto = true
opt-level = "s"

Your `.cargo/config.toml` will look like this:


[build]
target = "avr-atmega4809.json"

[unstable]
build-std = ["core"]

[registries]
snowgoons-crates = { index = "https://dl.cloudsmith.io/public/snowgoons/crates/cargo/index.git" }

[target.'cfg(target_arch = "avr")']
runner = "bin/arduino-nano-every-upload.sh"

You will also need a specific version of the Rust toolchain - `nightly-2021-01-07` - because later versions introduce AVR-specific bugs. You can configure this using the `rustup` command, but you can also just put a config file `rust-toolchain.toml` into the root of your project as well:


[toolchain]
channel = "nightly-2021-01-07"
components = ["rust-src"]

Don't forget to include the `avr-atmega4809.json` file described above, and to include the ATpacks in an appropriate place, and then, fingers crossed, you should be able to build and run!


Good luck :-).


1: /posts/2020-11-11-compiling-rust-for-arduino-nano-every-part-one.gmi

2: https://docs.arduino.cc/hardware/nano-every

3: https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/8-bit-mcus/avr-mcus/atmega4809

4: https://www.rust-lang.org/what/embedded

5: https://crates.io/crates/ruduino

6: https://www.microchip.com/

7: https://www.microchip.com/en-us/development-tools-tools-and-software/gcc-compilers-avr-and-arm

8: http://packs.download.atmel.com

9: https://crates.io/crates/ruduino

10: https://github.com/Rahix/avr-hal

11: https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/8-bit-mcus/avr-mcus/atmega4809

12: https://github.com/Rahix/avr-hal

13: https://cloudsmith.com

14: https://github.com/timwalls/avr-hal/tree/arduino-nano-every

15: https://github.com/timwalls/avr-device/tree/ATmega4809

16: https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/8-bit-mcus/avr-mcus/atmega4809

17: /posts/2020-11-11-compiling-rust-for-arduino-nano-every-part-one.gmi

--------------------

Home - gem.snowgoons.ro

-- Response ended

-- Page fetched on Fri May 10 10:24:18 2024