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.


No comments:

Post a Comment