What is it?

Recently I heard that LLVM for xtensa is working, so I tried to build Rust for ESP32.

(2019-05-16 Translated into English)

Source information is, here and here.

Environment

  • Ubuntu 18.04 LTS
  • esp-idf commit_id 5c88c5996dbde6208e3bec05abc21ff6cd822d26

Build LLVM

sudo apt-get install ninja-build

export MY_BUILD_ROOT="$HOME/.xtensa"
mkdir -p "$MY_BUILD_ROOT"
cd "$MY_BUILD_ROOT"

git clone https://github.com/espressif/llvm-xtensa.git
git clone https://github.com/espressif/clang-xtensa.git llvm-xtensa/tools/clang

mkdir llvm_build
cd llvm_build

# from https://gist.github.com/MabezDev/26e175790f84f2f2b0f9bca4e63275d1
cmake ../llvm-xtensa -DLLVM_TARGETS_TO_BUILD="Xtensa;X86" -DCMAKE_BUILD_TYPE=Release -G "Ninja"

# Take a while
cmake --build .

Check clang is working

 cd "$MY_BUILD_ROOT"

 cat <<EOF > test.c
 int main() {
   printf("Hello world\n");
 }
 EOF

./llvm_build/bin/clang -target xtensa -fomit-frame-pointer -S  test.c -o test.S
xtensa-esp32-elf-as test.S

file a.out
# → a.out: ELF 32-bit LSB relocatable, Tensilica Xtensa, version 1 (SYSV), not stripped

Bulid rustc

cd "$MY_BUILD_ROOT"

git clone git@github.com:MabezDev/rust-xtensa.git
cd rust-xtensa

mkdir "$MY_BUILD_ROOT/rust_build"
./configure --llvm-root="$MY_BUILD_ROOT/llvm_build" --prefix="$MY_BUILD_ROOT/rust_build"

# python 2 is required.

# seems to clone something. before help is invoked.
python ./x.py --help

# Take a while
python ./x.py build
python ./x.py install

Check rustc is working

Does rustc support xtensa?

"$MY_BUILD_ROOT/rust_build/bin/rustc" --print target-list | grep xtensa
# → xtensa-none-elf

Register own xtensa rustc to rustup

rustup toolchain link xtensa "$MY_BUILD_ROOT/rust_build"
rustup run xtensa rustc --print target-list | grep xtensa
# → xtensa-none-elf

Build a sample project

git clone git@github.com:lexxvir/esp32-hello.git  
cd esp32-hello
rmdir esp-idf
ln -s $IDF_PATH esp-idf

Manipulate .carg/config to work with your esp-idf environment.

diff --git a/.cargo/config b/.cargo/config
index f7c9d09..04a57ef 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -3,6 +3,7 @@ target = "xtensa-none-elf"

 [target.xtensa-none-elf]
 rustflags = [
+  "-C", "target-cpu=esp32",
   "-C", "save-temps",
   "-C", "link-arg=-nostdlib",

@@ -25,7 +26,7 @@ rustflags = [

   "-C", "link-arg=-Lesp-idf/components/esp32/ld",
   "-C", "link-arg=-Tesp32_out.ld",
-  "-C", "link-arg=-Tbuild/esp32/esp32.project.ld",
+  "-C", "link-arg=-Tbuild/esp32/esp32.common.ld",
   "-C", "link-arg=-Tesp32.rom.ld",
   "-C", "link-arg=-Tesp32.peripherals.ld",
   "-C", "link-arg=-Tesp32.rom.libgcc.ld",

add XARGO_RUST_SRC to setenv.sh

echo "export XARGO_RUST_SRC='$MY_BUILD_ROOT/rust-xtensa/src'" >> setenv.sh

Modify build.sh to use rustup run xtensa xargo

diff --git a/build.sh b/build.sh
index e7bcb40..70be266 100755
--- a/build.sh
+++ b/build.sh
@@ -5,7 +5,7 @@ source setenv.sh
 # export V=1
 make -j6 app

-cargo build --release #--verbose
+rustup run xtensa xargo build --release #--verbose

 $IDF_PATH/components/esptool_py/esptool/esptool.py \
        --chip esp32 \

Build the sample. target/xtensa-none-elf/release/esp32-hello.bin will appear if successfull.

./build.sh
ls target/xtensa-none-elf/release/esp32-hello.bin
# → target/xtensa-none-elf/release/esp32-hello.bin

Flash it.

python $IDF_PATH/components/esptool_py/esptool/esptool.py \
 --chip esp32 --port /dev/ttyUSB0 --baud 115200 \
 --before default_reset --after hard_reset write_flash -z --flash_mode dio \
 --flash_freq 40m --flash_size detect 0x1000 build/bootloader/bootloader.bin 0x10000 \
 target/xtensa-none-elf/release/esp32-hello.bin \
 0x8000 build/partitions_singleapp.bin

Connect a LED to GPIO2, you will see the LED is blinking.