tools: initial commit

This commit is contained in:
Juraj Michalek 2021-06-09 08:25:56 +02:00
commit e21fea775d
17 changed files with 1776 additions and 0 deletions

6
.cargo/config Normal file
View File

@ -0,0 +1,6 @@
[build]
target = "xtensa-esp32-none-elf"
[unstable]
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
.vscode/
build/
target/
sdkconfig.old
debug/
target/
Cargo.lock
**/*.rs.bk
*.pdb

13
CMakeLists.txt Normal file
View File

@ -0,0 +1,13 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32-hello-rust)
# The Xtensa Rust build includes compiler intrinsics that conflict with names
# in GCC. To avoid multiple definition errors, allow the linker to pick one of
# the available options and ignore the others.
if((${IDF_TARGET} MATCHES "esp32") OR (${IDF_TARGET} MATCHES "esp32s2"))
target_link_options(${CMAKE_PROJECT_NAME}.elf PRIVATE "-Wl,--allow-multiple-definition")
endif()

7
LICENSE Normal file
View File

@ -0,0 +1,7 @@
Copyright 2021 Espressif Systems (Shanghai) CO LTD
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

128
README.md Normal file
View File

@ -0,0 +1,128 @@
# Rust ESP32 Example
An example project demonstrating integration with Rust for the ESP32-S2 and ESP32-C3 microcontrollers.
This example starts a FreeRTOS task to call a function in Rust and display the results in C.
## Setup
First, install the ESP-IDF SDK as shown in the [Get Started](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) guides. For best support of the ESP32-C3, install the SDK from the master branch.
### ESP32 and ESP32-S series
To support the Xtensa instruction set, build and install custom LLVM and Rust toolchains as shown in the [Rust On Xtensa](RustOnXtensa.md) guide.
### ESP32-C3
Install the RISCV target for Rust:
```sh
rustup target add riscv32i-unknown-none-elf
```
## Build Rust library
This will eventually be folded into the main CMake build, but for now, build the library separately.
### ESP32 or ESP32-S2
To build the Rust library, run:
```sh
cd rustlib
xargo build --release
cd ..
```
Build with manual configuration of the target:
```sh
export RUSTC=/usr/local/xtensa/rust/bin/rustc
export XARGO_RUST_SRC=/usr/local/xtensa/rust/library
cd rustlib
xargo build --release --target=xtensa-esp32-none-elf
cd ..
```
### ESP32-C3
To build the Rust library, run:
```sh
cd rustlib
cargo build --release
cd ..
```
Build with manual configuration of the target:
```sh
unset RUSTC
unset XARGO_RUST_SRC
cd rustlib
cargo build --release --target=riscv32i-unknown-none-elf
cd ..
```
## Configure
First ensure that the environment variables for the ESP32 SDK are properly set up. If you have followed the instructions in the Getting Started guide, activate the environment with the `get_idf` alias:
```sh
get_idf
```
Next, configure the project for the desired MCU.
For the ESP32:
```sh
idf.py set-target esp32
idf.py menuconfig
```
For the ESP32-S2:
```sh
idf.py set-target esp32s2
idf.py menuconfig
```
For the ESP32-C3:
```sh
idf.py set-target esp32c3
idf.py menuconfig
```
## Build
Build the project by running:
```sh
idf.py build
```
## Flash
Flash the compiled binary by running:
```sh
idf.py -p /dev/cu.SLAB_USBtoUART flash
```
## Monitor
Connect to the UART over USB port to monitor the application console:
```sh
idf.py -p /dev/cu.SLABtoUART monitor
```
To exit the monitor, press `Ctrl-]`.
## Debugging on ESP32-WROVER-KIT
```sh
openocd -f board/esp32-wrover-kit-3.3v.cfg
```

137
RustOnXtensa.md Normal file
View File

@ -0,0 +1,137 @@
# Rust on ESP32 (Xtensa)
## Quick start
The installation process of ready to use custom build of Rust and LLVM is described here: https://dl.espressif.com/dl/idf-rust/
Following text describes the build process when building LLVM and Rust from the scratch.
## Using Rust for ESP32 Development
Given the popularity of the ESP32 chip, there has been an interest in developing applications for it using the Rust programming language. The ESP32 has traditionally used the Xtensa instruction set, which is not officially supported by Rust.
A new version of the ESP32, the ESP32-C3, has recently been released. The new C3 variant is based on a RISC-V architecture. RISC-V is a supported architecture for LLVM and Rust. This document, however, will concern itself with the more popular and, at the time of writing, more powerful Xtensa-based systems.
## Build LLVM for Xtensa
Although the Xtensa instruction set is not supported by the LLVM project, Espressif has continued to maintain a fork. Unfortunately, these changes have not been migrated upstream into the main project. This would be a prerequisite before official support in Rust is possible.
The fork is hosted at [espressif/llvm-project](https://github.com/espressif/llvm-project)
The Rust for for Xtensa fork (see below) includes the LLVM repo as a submodule, so it is not necessary to work with this repository directly. However, it uses an older version of the code. To use the most up to date, we can build the toolchain separately from the main repository.
Note, however, that while clang will successfully compile code, the toolchain is not able to link final executables for either the host or target systems.
```
% git clone https://github.com/espressif/llvm-project.git
% cd llvm-project
% mkdir build
% cd build
% cmake ../llvm -G "Ninja" -DLLVM_TARGETS_TO_BUILD="X86" -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="Xtensa" -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_INSTALL_PREFIX=/usr/local/xtensa/llvm -DCMAKE_BUILD_TYPE=Release
% ninja
% sudo ninja install
```
The Xtensa target support can be verified by printing the supported target architectures.
```
% /usr/local/xtensa/llvm/bin/llc --version
LLVM (http://llvm.org/):
LLVM version 11.0.0
Optimized build.
Default target: x86_64-apple-darwin20.3.0
Host CPU: cascadelake
Registered Targets:
x86 - 32-bit X86: Pentium-Pro and above
x86-64 - 64-bit X86: EM64T and AMD64
xtensa - Xtensa 32
```
The available CPUs and features for the architecture can also be queried.
```
% /usr/local/xtensa/llvm/bin/llc -march=xtensa -mattr=help
Available CPUs for this target:
esp32 - Select the esp32 processor.
esp32-s2 - Select the esp32-s2 processor.
esp8266 - Select the esp8266 processor.
generic - Select the generic processor.
Available features for this target:
atomctl - Enable Xtensa ATOMCTL option.
atomctl - Enable Xtensa MEMCTL option.
bool - Enable Xtensa Boolean extension.
coprocessor - Enable Xtensa Coprocessor option.
debug - Enable Xtensa Debug option.
density - Enable Density instructions.
dfpaccel - Enable Xtensa Double Precision FP acceleration.
div32 - Enable Xtensa Div32 option.
exception - Enable Xtensa Exception option.
exception - Enable Xtensa HighPriInterrupts option.
extendedl32r - Enable Xtensa Extended L32R option.
fp - Enable Xtensa Single FP instructions.
interrupt - Enable Xtensa Interrupt option.
loop - Enable Xtensa Loop extension.
mac16 - Enable Xtensa MAC16 instructions.
miscsr - Enable Xtensa Miscellaneous SR option.
mul32 - Enable Xtensa Mul32 option.
mul32high - Enable Xtensa Mul32High option.
nsa - Enable Xtensa NSA option.
prid - Enable Xtensa Processor ID option.
regprotect - Enable Xtensa Region Protection option.
rvector - Enable Xtensa Relocatable Vector option.
s32c1i - Enable Xtensa S32C1I option.
sext - Enable Xtensa Sign Extend option.
threadptr - Enable Xtensa THREADPTR option.
timerint - Enable Xtensa Timer Interrupt option.
windowed - Enable Xtensa Windowed Register option.
Use +feature to enable a feature, or -feature to disable it.
For example, llc -mcpu=mycpu -mattr=+feature1,-feature2
```
## Build Rust for Xtensa
While it should be possible to skip the internal build of LLVM libraries and reuse the LLVM toolchain built above, that didn't work successfully. Instead, use the built in version of the toolchain.
```
% git clone https://github.com/MabezDev/rust-xtensa.git
% cd rust-xtensa
% git submodule update --init --recursive
% ./configure --experimental-targets=Xtensa --prefix=/usr/local/xtensa/rust
% python x.py build --stage=2
```
The install option for `x.py` runs into some problems if sudo permissions are needed (requiring cargo dependencies to be vendored), so create an install directory owned by the user.
```
% sudo mkdir -p /usr/local/xtensa/rust
% sudo chown `whoami` /usr/local/xtensa/rust
% python x.py install
```
Finally, the library source tree needed by `xargo` to build the core library crates needs to be copied into the installation as well.
```
% sudo cp Cargo.* /usr/local/xtensa/rust/
% sudo cp -r library /usr/local/xtensa/rust/
```
Finally, install `xargo` to wrap the normal `cargo` command.
```sh
% cargo install xargo
```
## Using Rust
To build a Rust library for Xtensa, set up the environment for `xargo` to find the custom Rust toolchain and build with `xargo` instead of `cargo`.
```sh
export RUSTC=/usr/local/xtensa/rust/bin/rustc
export XARGO_RUST_SRC=/usr/local/xtensa/rust/library
xargo build --release --target=xtensa-esp32-none-elf
```

7
main/CApi.h Normal file
View File

@ -0,0 +1,7 @@
/*
* C functions exposed to Rust.
*/
#include <stdbool.h>
extern bool validate_param_in_c(int param, int value);

16
main/CMakeLists.txt Normal file
View File

@ -0,0 +1,16 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "")
set(CargoBuildType "release")
# Import the Rust library.
if((${IDF_TARGET} MATCHES "esp32") OR (${IDF_TARGET} MATCHES "esp32s2"))
set(ToolchainTarget "xtensa-esp32-none-elf")
elseif(${IDF_TARGET} MATCHES "esp32c3")
set(ToolchainTarget "riscv32i-unknown-none-elf")
endif()
add_prebuilt_library(rustlib "../rustlib/target/${ToolchainTarget}/${CargoBuildType}/librustlib.a")
# 'main' calls a function from the library, so link it to 'main'
target_link_libraries(${COMPONENT_LIB} PRIVATE rustlib)

5
main/RustApi.h Normal file
View File

@ -0,0 +1,5 @@
/*
* Rust functions exposed to C.
*/
extern int add_in_rust(int x, int y);

5
main/component.mk Normal file
View File

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

64
main/main.c Normal file
View File

@ -0,0 +1,64 @@
/* Hello World Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "CApi.h"
#include "RustApi.h"
void app_main(void)
{
printf("Hello world!\n");
/* Print chip information */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU cores, WiFi%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
printf("silicon revision %d, ", chip_info.revision);
printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
printf("Free heap: %d\n", esp_get_free_heap_size());
int x = 2;
int y = 3;
int sum = add_in_rust(x, y);
printf("Rust calculated %d + %d = %d\n\n", x, y, sum);
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}
// bool validate_param_in_c(const char *param, int value)
// {
// printf("C validated %s = %d\n", param, value);
// return true;
// }
bool validate_param_in_c(int param, int value)
{
printf("C validated %d = %d\n", param, value);
return true;
}

28
rustlib/Cargo.toml Normal file
View File

@ -0,0 +1,28 @@
[package]
name = "rustlib"
version = "0.1.0"
authors = ["Espressif Systems CO LTD", "June Life Inc"]
edition = "2018"
[lib]
crate-type = ["staticlib"]
#crate-type = ["rlib", "staticlib"]
[build-dependencies]
bindgen = "0.58"
[profile.dev]
# Optimization level 1 is similar to "g" in gcc/clang, although for some reason
# Cargo doesn't support that flag.
#opt-level = 1
opt-level = "s"
[profile.release]
debug = true
opt-level = "z"
#debug = true
#lto = true
#opt-level = "z"
#codegen-units = 1
#panic = "abort"

3
rustlib/bindings.rs Normal file
View File

@ -0,0 +1,3 @@
/* automatically generated by rust-bindgen 0.58.1 */
pub const true_ : u32 = 1 ; pub const false_ : u32 = 0 ; pub const __bool_true_false_are_defined : u32 = 1 ; extern "C" { pub fn validate_param_in_c (param : :: std :: os :: raw :: c_int , value : :: std :: os :: raw :: c_int) -> bool ; }

49
rustlib/build.rs Normal file
View File

@ -0,0 +1,49 @@
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
fn main() {
let target = env::var("TARGET").unwrap();
// let cargo_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
// fs::remove_dir_all(&out_dir).unwrap();
// fs::create_dir_all(&out_dir).unwrap();
// run_cbindgen(&cargo_dir, &out_dir);
run_bindgen(&target, &out_dir);
}
// fn run_cbindgen(_cargo_dir: &Path, _out_dir: &Path) {
// TODO: Run cbindgen
// }
fn run_bindgen(target: &str, out_dir: &Path) {
// if target == "xtensa-esp32-none-elf" {
// env::set_var("LLVM_CONFIG_PATH", "/usr/local/xtensa/llvm/bin/llvm-config");
// }
let mut builder = bindgen::Builder::default();
builder = builder.header("../main/CApi.h");
builder = builder.use_core();
builder = builder.ctypes_prefix("ffi");
println!("Target: {}", target);
match target {
"riscv32i-unknown-none-elf" => {
builder = builder.clang_arg("--target=riscv32");
}
"xtensa-esp32-none-elf" => {
builder = builder.clang_arg("--target=xtensa-esp32-elf");
}
_ => {
panic!("Unexpect target archtitecture: {}", &target);
}
}
let bindings = builder.generate().expect("Couldn't generate bindings!");
bindings
.write_to_file(out_dir.join("bindings.rs"))
.expect("Couldn't save bindings!");
}

1
rustlib/cbindgen.toml Normal file
View File

@ -0,0 +1 @@
language = "C"

42
rustlib/src/lib.rs Normal file
View File

@ -0,0 +1,42 @@
// #![no_std]
use core::panic::PanicInfo;
pub mod ffi {
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
pub type c_char = u8;
pub type c_int = i32;
}
pub mod sys {
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::ffi;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
use crate::sys::*;
// #[panic_handler]
// fn panic(_info: &PanicInfo) -> ! {
// loop {}
// }
#[no_mangle]
pub extern "C" fn add_in_rust(x: i32, y: i32) -> i32 {
extern "C" {
fn validate_param_in_c(param: i32, value: i32) -> i32;
}
unsafe {
validate_param_in_c(0, x);
validate_param_in_c(2, y);
}
x + y
}

1255
sdkconfig Normal file

File diff suppressed because it is too large Load Diff