Friday, 2 December 2016

Target practice

Now, as we know, the original Raspberry Pi uses an ARM11 core, which is based on the ARMv6 architecture. The Raspberry Pi 2 uses quad Cortex-A7 cores (although it's actually just been updated to have the same chip as the Pi 3 for volume reasons) which are based on the ARMv7-A architecture while the Pi 3 uses quad Cortex-A53s which are based on the ARMv8-A architecture.

So what do this have to do with Rust?

Well, in two ways really. One is that the compiler is downloaded as a binary, and it the instructions encoded into the machine code need to be ones that that processor core can understand and execute. Helpfully, the ARM architectures are backwards compatible, so the Pi 3 can run code compiled for an original Pi 2 and both can run code compiled for an original Pi.

The second is in the choice of instructions the compiler chooses to emit. Now, by default, you would expect this to be the same as the compiler itself was compiled to - after all, 9 times out of 10 you're going to run the code on the machine you're using to compile it. But that 1 time in 10, someone may well be cross-compiling. For example, I do a lot of embedded development and I will use my x86_64 laptop to emit binaries which will execute on the ARMv7E-M architecture of my favourite Cortex-M4F based embedded development board.

So, with that in mine, some advice.

When you run rustup on a Raspberry Pi (or any other Linux machine), it will detect the processor architecture and download the appropriate compiler. That compiler will then default to outputting binaries which target the same architecture. Here's the list of available architecture/OS/C-library combinations for I could install today (from https://static.rust-lang.org/dist/channel-rust-stable.toml):

rust-1.13.0-aarch64-unknown-linux-gnu.tar.gz
rust-1.13.0-arm-unknown-linux-gnueabi.tar.gz
rust-1.13.0-arm-unknown-linux-gnueabihf.tar.gz
rust-1.13.0-armv7-unknown-linux-gnueabihf.tar.gz
rust-1.13.0-i686-apple-darwin.tar.gz
rust-1.13.0-i686-pc-windows-gnu.tar.gz
rust-1.13.0-i686-pc-windows-msvc.tar.gz
rust-1.13.0-i686-unknown-linux-gnu.tar.gz
rust-1.13.0-x86_64-apple-darwin.tar.gz
rust-1.13.0-x86_64-pc-windows-gnu.tar.gz
rust-1.13.0-x86_64-pc-windows-msvc.tar.gz
rust-1.13.0-x86_64-unknown-freebsd.tar.gz
rust-1.13.0-x86_64-unknown-linux-gnu.tar.gz
rust-1.13.0-x86_64-unknown-netbsd.tar.gz


There's four ARM toolchains listed. What gives?

Well aarch64 is the 64-bit ARMv8-A toolchain. You'd use this if your Raspberry Pi 3 was running that new 64-bit release of OpenSUSE or some other 64-bit Linux distribution. It probably isn't.

The armv7 toolchain will run on a Pi 2 or Pi 3, but not a Pi 1 or Zero. This will be automatically selected if you run rustup on a Pi 2 or Pi 3 so be warned if you swap SD cards between various models - this will instantly crash on a Pi 1 or Pi Zero.

The two arm versions are for the two floating point ABIs (application binary interface) - basically, whether the code expects floating point arguments to functions to be placed in hardware floating point registers (gnueabihf) or in standard integer registers (gnueabi). Raspbian requires gnueabihf, while standard Debian for the ARMv6 I believe requires gnueabi. That's one of the reasons for the Raspbian recompile of Debian in the first place - gnueabihf is faster, but it does require your CPU to have hardware floating point registers (which the original Pi does).

Now if you've set yourself up on a Pi 3 but want to run code on a Pi Zero, you can keep you shiny fast compiler but tell it to emit code for a different 'target', like this:

pi@boron:~/livedemo $ rustup target add arm-unknown-linux-gnueabihf
info: downloading component 'rust-std' for 'arm-unknown-linux-gnueabihf'
 61.0 MiB /  61.0 MiB (100 %)   4.1 MiB/s ETA:   0 s              
info: installing component 'rust-std' for 'arm-unknown-linux-gnueabihf'
pi@boron:~/livedemo $ cargo build --target=arm-unknown-linux-gnueabihf
    Compiling livedemo v0.1.0 (file:///home/pi/livedemo)
    Finished debug [unoptimized + debuginfo] target(s) in 7.8 secs
pi@boron:~/livedemo $ ls -l target/
total 8
drwxr-xr-x 3 pi pi 4096 Dec  2 15:49 arm-unknown-linux-gnueabihf
drwxr-xr-x 7 pi pi 4096 Dec  2 16:36 debug
pi@boron:~/livedemo $

Now you have a Pi Zero compatible binary in target/arm-unknown-linux-gnueabihf/debug.

Your other option, if you want to share your SD card between a Pi 1 and a Pi 2/3 is to install the arm toolchain and remove the armv7 toolchain. You'd use a command like "rustup toolchain install beta-arm-unknown-linux-gnueabihf" to do that - I'll leave it as an exercise for the reader.


Bug in Rust 1.13 for ARM and how to work around it

There's a nasty code generation bug in Rust 1.13 stable. Because ARM isn't considered a 'tier-1' platform for Rust at this time, the team have decided to put the fix into 1.14 beta (and 1.15 nightly) but not up-issue 1.13. Unfortunately this means if you follow my last post, you'll get a compiler that doesn't work properly when using floating point functions.

The best fix is probably switch to 1.14 beta until 1.14 stable comes out (around 22 December). You can do this as follows:

pi@boron:~ $ rustup toolchain install beta
info: syncing channel updates for 'beta-armv7-unknown-linux-gnueabihf'
info: downloading component 'rustc'
 47.0 MiB /  47.0 MiB (100 %)   6.2 MiB/s ETA:   0 s                
info: downloading component 'rust-std'
 60.3 MiB /  60.3 MiB (100 %)   6.2 MiB/s ETA:   0 s                
info: downloading component 'cargo'
  4.2 MiB /   4.2 MiB (100 %) 737.2 KiB/s ETA:   0 s                
info: installing component 'rustc'
info: installing component 'rust-std'
info: installing component 'cargo'

    beta-armv7-unknown-linux-gnueabihf installed - rustc 1.14.0-beta.2 (e627a2e6e 2016-11-16)

pi@boron:~ $ rustup default beta
info: using existing install for 'beta-armv7-unknown-linux-gnueabihf'
info: default toolchain set to 'beta-armv7-unknown-linux-gnueabihf'

    beta-armv7-unknown-linux-gnueabihf unchanged - rustc 1.14.0-beta.2 (e627a2e6e 2016-11-16)

pi@boron:~ $ rustc -Vv
rustc 1.14.0-beta.2 (e627a2e6e 2016-11-16)
binary: rustc
commit-hash: e627a2e6edbc7b7fd205de8ca7c86cff76655f4d
commit-date: 2016-11-16
host: armv7-unknown-linux-gnueabihf
release: 1.14.0-beta.2
LLVM version: 3.9
pi@boron:~ $
You can then switch back to stable once 1.14 has been released, or if they do put out a point release (unlikely at this point).

Sunday, 30 October 2016

Raspberry Rust

I'm a big fan of Rust, the new systems programming language from Mozilla, and I'm a big fan of the Raspberry Pi, so I thought I'd write a few notes on running Rust on the Raspberry Pi!

Once upon a time installing Rust was a little but messy, but now there's rustup-rs - a Rust install tool written in Rust!

Step 1. Get a Raspberry Pi, boot it up, and get yourself a terminal window.

Step 2. Run this command:

curl https://sh.rustup.rs -sSf | sh

The installer will check out your architecture (armv7l for a Pi 2 or Pi 3, or armv6 for an original Pi) and download a pre-compiled copy of Rust, ready to run.

pi@boron:~ $ curl https://sh.rustup.rs -sSf | sh

info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust programming 
language, and its package manager, Cargo.

It will add the cargo, rustc, rustup and other commands to Cargo's bin 
directory, located at:

  /home/pi/.cargo/bin

This path will then be added to your PATH environment variable by modifying the
profile file located at:

  /home/pi/.profile

You can uninstall at any time with rustup self uninstall and these changes will
be reverted.

WARNING: This is beta software.

Current installation options:

    default host triple: armv7-unknown-linux-gnueabihf
      default toolchain: stable
   modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation

<press 1 followed by enter here>

info: syncing channel updates for 'stable-armv7-unknown-linux-gnueabihf'
info: downloading component 'rustc'
 44.7 MiB /  44.7 MiB (100 %)   3.7 MiB/s ETA:   0 s                
info: downloading component 'rust-std'
 61.4 MiB /  61.4 MiB (100 %)   4.0 MiB/s ETA:   0 s                 
info: downloading component 'rust-docs'
  7.5 MiB /   7.5 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: downloading component 'cargo'
  4.1 MiB /   4.1 MiB (100 %) 933.3 KiB/s ETA:   0 s                
info: installing component 'rustc'
info: installing component 'rust-std'
info: installing component 'rust-docs'
info: installing component 'cargo'
info: default toolchain set to 'stable'

   stable installed - rustc 1.12.1 (d4f39402a 2016-10-19)

Rust is installed now. Great!

To get started you need Cargo's bin directory in your PATH environment 
variable. Next time you log in this will be done automatically.

To configure your current shell run source $HOME/.cargo/env.

Step 3. Write some Rust! First, use Cargo (the Rust package manager) to create you a new 'crate'. A 'crate' is what Rust calls packages, and cargo is how you download crates and build them. Yes, it's all very pun-tastic. Anyway, we use the 'cargo new --bin' command to create a new binary crate - that is, it's a crate that produces a program we can run, as opposed to a library crate we can use in other crates later on.

pi@boron:~ $ source $HOME/.cargo/env
pi@boron:~ $ cargo new --bin my_first_project
     Created binary (application) `my_first_project` project
pi@boron:~ $ cd my_first_project/
pi@boron:~/my_first_project $ cargo run
   Compiling my_first_project v0.1.0 (file:///home/pi/my_first_project)
    Finished debug [unoptimized + debuginfo] target(s) in 8.79 secs
     Running `target/debug/my_first_project`
Hello, world!
pi@boron:~/my_first_project $


Step 4. So what did we just do? Well 'cargo new' create a Hello World example for us, and then 'cargo run' compiled our source code into an executable and then executed it. Let's have a look at the example:
pi@boron:~/my_first_project $ cat src/main.rs
fn main() {
    println!("Hello, world!");
}
If we'd created a library crate instead of a binary create, it would have created a 'lib.rs' instead of a 'main.rs'.

Step 5. Go learn Rust! Hopefully you've seen that Rust on the Raspberry Pi is very easy to get started with, and there's nothing Pi specific about it. Go and look at the Book, and the API guide, the User Forum, the /r/rust Reddit. If you've got 30 minutes, I've got a brief presentation on Rust from the point of view of a C programmer, and there are many more on YouTube. Stop wasting precious CPU cycles on the Python interpreter and stop writing buggy C programs full of hard-to-find race hazards and buffer overflows - Rust ftw!

Sunday, 23 October 2016

Freescale Kinetis

It appears some of Freescale's Kinetis line of ARM microcontrollers are available in 5V variants. Specifically, the KE02, the KE04 and the KE06.






Lots of I/O, PWM, USB interface/debug via a separate (actually much more powerful) MCU, Cortex-M0+ (which means Rust support, via the thumbv6m-none-eabi target). Could this be the 5V replacement for the Stellaris Launchpad I've been looking for?