Skip to content

Add devicetree support to Rust on Zephyr #76074

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 75 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
a0611a1
rust: Initial config option
d3zd3z Jul 15, 2024
79eb8a2
rust: Simple main wrapper
d3zd3z Jul 15, 2024
6646068
rust: Basic `zephyr` crate
d3zd3z Jul 15, 2024
62af97a
rust: Add cmake support for building rust apps
d3zd3z Jul 15, 2024
775a3a0
rust: Simple rust hello_world application
d3zd3z Jul 15, 2024
564d96d
MAINTAINERS: Add Rust to maintainers file
d3zd3z Jul 15, 2024
191eca3
rust: Move Kconfig bool values into crate
d3zd3z Jul 15, 2024
98f0291
rust: Make Kconfig values available to Rust code
d3zd3z Jul 15, 2024
62770bf
samples: rust: Access CONFIG_BOARD
d3zd3z Jul 15, 2024
6f55e18
doc: languages: Add Rust documentation
d3zd3z Jul 16, 2024
cf88e6f
samples: rust: Rename hello
d3zd3z Jul 16, 2024
6052ca4
doc: Use proper quote block for shell
d3zd3z Jul 16, 2024
c95dc43
rust: Support negative Kconfig int values
d3zd3z Jul 17, 2024
db90a84
rust: Mark RUST support as experimental
d3zd3z Jul 17, 2024
285c9d5
cmake: rust: Use proper FPU config
d3zd3z Jul 18, 2024
a8d1745
doc: rust: Fix code block
d3zd3z Jul 18, 2024
2f39272
doc: rust: Use real config settings
d3zd3z Jul 18, 2024
f883380
cmake: rust: Fix Cortex-M4 non-fp target
d3zd3z Jul 18, 2024
b72fdeb
rust: Fix prototype for rust_main
d3zd3z Jul 18, 2024
2391a2d
cmake: rust: Fix typo
d3zd3z Jul 23, 2024
3aa32de
rust: zephyr-build: Fix logic
d3zd3z Jul 23, 2024
e259073
samples: rust: Ignore Cargo.lock files
d3zd3z Jul 23, 2024
879fb18
lib: rust: Add wrapper for k_str_out
d3zd3z Jul 23, 2024
0ee052a
rust: Implement `printk` and `printkln`
d3zd3z Jul 23, 2024
1bd34ac
samples: rust: Use printkln macro
d3zd3z Jul 23, 2024
e9eda36
rust: Only provide Rust on supported platforms
d3zd3z Jul 24, 2024
511287a
samples: rust: Remove RTT from the sample
d3zd3z Jul 24, 2024
d857274
samples: rust: Enable Rust hello as sample
d3zd3z Jul 24, 2024
8905f88
rust: Add support for some RISC-V targets
d3zd3z Jul 24, 2024
0254859
rust: Ignore possible left files
d3zd3z Jul 26, 2024
0d3c4bf
rust: Create zephyr-sys for low-level bindings
d3zd3z Jul 26, 2024
2d88a85
rust: Use zephyr-sys to get bindings
d3zd3z Jul 26, 2024
165aaa2
rust: Convert k_str_out to bindgen one
d3zd3z Jul 26, 2024
b38b50a
include: Add dummy field for Rust as well as CPP
d3zd3z Jul 26, 2024
4cd5a1a
cmake: rust: Compile the bindgen wrapper
d3zd3z Jul 26, 2024
d0e08f9
rust: Ignore unknown cfgs in the zephyr crate
d3zd3z Jul 26, 2024
8b80df8
rust: Pass binary output dir to Rust
d3zd3z Jul 18, 2024
b697ce0
rust: zephyr-build: Conversion of Device Tree
d3zd3z Jul 18, 2024
0733956
rust: zephyr: Use generated DT module
d3zd3z Jul 18, 2024
7a88cbe
rust: Add allocator support
d3zd3z Jul 18, 2024
b4b81d6
samples: rust: Simplify to use allocation
d3zd3z Jul 18, 2024
5e7f721
rust: Provide a `println` macro when alloc is available
d3zd3z Jul 18, 2024
4a917f0
samples: rust: Simplify the rust hello
d3zd3z Jul 18, 2024
7ae0ab4
rust: Messy hand-generated sys binding
d3zd3z Jul 19, 2024
0c31404
samples: rust: Add blinky
d3zd3z Jul 19, 2024
2c7c609
rust: Add augments to Rust DT generation
d3zd3z Jul 19, 2024
e26238e
rust: sys: Export the gpio based types
d3zd3z Jul 19, 2024
8ac387b
rust: sys: Make gpio methods require `mut`
d3zd3z Jul 19, 2024
6a79dce
sample: rust: blinky: Use DT instead of unsafe
d3zd3z Jul 19, 2024
5773c3b
samples: rust: Remove constants from blinky
d3zd3z Jul 19, 2024
868ecc1
WIP
d3zd3z Jul 23, 2024
dd41c2d
rust: Work around rust's alloc and userspace
d3zd3z Jul 24, 2024
c42249b
fix merge errors
d3zd3z Jul 24, 2024
8cd998a
cmake: rust: Fix typo in cmake
d3zd3z Jul 26, 2024
882a950
rust: Adapt DT to new zephyr-sys
d3zd3z Jul 26, 2024
1a21d86
rust: Add a simple rust logger using printk
d3zd3z Jul 26, 2024
c0b196a
samples: rust: blink: Adapt to newer code
d3zd3z Jul 26, 2024
41027aa
rust: Work around clang not defining __SOFTFP__
d3zd3z Jul 29, 2024
ca583c8
rust: Provide critical-section for Zephyr
d3zd3z Jul 29, 2024
0e3eb42
rust: Allow GpioPin to not be used
d3zd3z Jul 29, 2024
70a0d93
rust: Add zephyr-sys into the Rust builds
d3zd3z Jul 31, 2024
9e654a8
rust: Add a kconfig RUST_CHECK_KOBJ_INIT
d3zd3z Jul 31, 2024
24894d2
rust: zephyr-sys: additional bindings
d3zd3z Jul 31, 2024
3c0df4a
rust: Add kobject support with threads
d3zd3z Jul 31, 2024
f9dae54
samples: rust: philosopher
d3zd3z Jul 31, 2024
ddf39df
rust: Support closure thread creation
d3zd3z Jul 31, 2024
be8d953
samples: rust: Use `spawn` to start child threads
d3zd3z Jul 31, 2024
b80a750
rust: Disable .noinit for now
d3zd3z Jul 31, 2024
79086a3
rust: Add a simple safe wrapper for k_mutex
d3zd3z Jul 31, 2024
10408a7
samples: rust: Change philosophers to use sys::Mutex
d3zd3z Jul 31, 2024
6bc8776
linker: Include the .ARM.extab section for Rust
d3zd3z Jul 31, 2024
5c9067e
rust: Implement sync::Mutex
d3zd3z Jul 31, 2024
80fc45c
rust: Put K_FOREVER and K_NO_WAIT in sys
d3zd3z Jul 31, 2024
773d278
rust: Allow sys::Mutex to be cloned
d3zd3z Jul 31, 2024
01339a1
samples: rust: Use sync::Mutex for philosopher demo
d3zd3z Jul 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions MAINTAINERS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3039,6 +3039,17 @@ Retention:
labels:
- "area: Retention"

Rust:
status: maintained
maintainers:
- d3zd3z
files:
- cmake/modules/rust.cmake
- lib/rust/
- samples/rust/
labels:
- "area: Rust"

Samples:
status: maintained
maintainers:
Expand Down
187 changes: 187 additions & 0 deletions cmake/modules/rust.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# SPDX-License-Identifier: Apache-2.0

# Rust make support

# Zephyr targets are defined through Kconfig. We need to map these to
# an appropriate llvm target triple. This sets `RUST_TARGET` in the
# parent scope, or an error if the target is not yet supported by
# Rust.
function(_rust_map_target)
# Map Zephyr targets to LLVM targets.
if(CONFIG_CPU_CORTEX_M)
if(CONFIG_CPU_CORTEX_M0 OR CONFIG_CPU_CORTEX_M0PLUS OR CONFIG_CPU_CORTEX_M1)
set(RUST_TARGET "thumbv6m-none-eabi" PARENT_SCOPE)
elseif(CONFIG_CPU_CORTEX_M3)
set(RUST_TARGET "thumbv7m-none-eabi" PARENT_SCOPE)
elseif(CONFIG_CPU_CORTEX_M4)
if(CONFIG_FP_HARDABI OR FORCE_FP_HARDABI)
set(RUST_TARGET "thumbv7em-none-eabihf" PARENT_SCOPE)
else()
set(RUST_TARGET "thumbv7em-none-eabi" PARENT_SCOPE)
endif()
elseif(CONFIG_CPU_CORTEX_M23)
set(RUST_TARGET "thumbv8m.base-none-eabi" PARENT_SCOPE)
elseif(CONFIG_CPU_CORTEX_M33 OR CONFIG_CPU_CORTEX_M55)
# Not a typo, Zephyr, uses ARMV7_M_ARMV8_M_FP to select the FP even on v8m.
if(CONFIG_FP_HARDABI OR FORCE_FP_HARDABI)
set(RUST_TARGET "thumbv8m.main-none-eabihf" PARENT_SCOPE)
else()
set(RUST_TARGET "thumbv8m.main-none-eabi" PARENT_SCOPE)
endif()

# Todo: The M55 is thumbv8.1m.main-none-eabi, which can be added when Rust
# gain support for this target.
else()
message(FATAL_ERROR "Unknown Cortex-M target.")
endif()
elseif(CONFIG_RISCV)
if(CONFIG_RISCV_ISA_RV64I)
# TODO: Should fail if the extensions don't match.
set(RUST_TARGET "riscv64imac-unknown-none-elf" PARENT_SCOPE)
elseif(CONFIG_RISCV_ISA_RV32I)
# TODO: We have multiple choices, try to pick the best.
set(RUST_TARGET "riscv32i-unknown-none-elf" PARENT_SCOPE)
else()
message(FATAL_ERROR "Rust: Unsupported riscv ISA")
endif()
else()
message(FATAL_ERROR "Rust: Add support for other target")
endif()
endfunction()

function(get_include_dirs target dirs)
get_target_property(include_dirs ${target} INTERFACE_INCLUDE_DIRECTORIES)
if(include_dirs)
set(${dirs} ${include_dirs} PARENT_SCOPE)
else()
set(${dirs} "" PARENT_SCOPE)
endif()
endfunction()

function(rust_cargo_application)
# For now, hard-code the Zephyr crate directly here. Once we have
# more than one crate, these should be added by the modules
# themselves.
set(LIB_RUST_CRATES zephyr zephyr-sys zephyr-build)

get_include_dirs(zephyr_interface include_dirs)

get_target_property(include_dirs, zephyr_interface INTERFACE_INCLUDE_DIRECTORIES)
get_property(include_defines TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_DEFINITIONS)
message(STATUS "Includes: ${include_dirs}")
message(STATUS "Defines: ${include_defines}")

_rust_map_target()
message(STATUS "Building Rust llvm target ${RUST_TARGET}")

# TODO: Make sure RUSTFLAGS is not set.

# TODO: Let this be configurable, or based on Kconfig debug?
set(RUST_BUILD_TYPE debug)
set(BUILD_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${RUST_TARGET}/${RUST_BUILD_TYPE}")

set(CARGO_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/rust/target")
set(RUST_LIBRARY "${CARGO_TARGET_DIR}/${RUST_TARGET}/${RUST_BUILD_TYPE}/librustapp.a")
set(SAMPLE_CARGO_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/rust/sample-cargo-config.toml")

# The generated C binding wrappers. These are bindgen-generated wrappers for the inline functions
# within Zephyr.
set(WRAPPER_FILE "${CMAKE_CURRENT_BINARY_DIR}/rust/wrapper.c")

# To get cmake to always invoke Cargo requires a bit of a trick. We make the output of the
# command a file that never gets created. This will cause cmake to always rerun cargo. We
# add the actual library as a BYPRODUCTS list of this command, otherwise, the first time the
# link will fail because it doesn't think it knows how to build the library. This will also
# cause the relink when the cargo command actually does rebuild the rust code.
set(DUMMY_FILE "${CMAKE_BINARY_DIR}/always-run-cargo.dummy")

# For each module in zephyr-rs, add entry both to the .cargo/config template and for the
# command line, since either invocation will need to see these.
set(command_paths)
set(config_paths "")
message(STATUS "Processing crates: ${ZEPHYR_RS_MODULES}")
foreach(module IN LISTS LIB_RUST_CRATES)
message(STATUS "module: ${module}")
set(config_paths
"${config_paths}\
${module}.path = \"${ZEPHYR_BASE}/lib/rust/${module}\"
")
list(APPEND command_paths
"--config"
"patch.crates-io.${module}.path=\\\"${ZEPHYR_BASE}/lib/rust/${module}\\\""
)
endforeach()

# Write out a cargo config file that can be copied into `.cargo/config.toml` (or made a
# symlink) in the source directory to allow various IDE tools and such to work. The build we
# invoke will override these settings, in case they are out of date. Everything set here
# should match the arguments given to the cargo build command below.
file(WRITE ${SAMPLE_CARGO_CONFIG} "
# This is a generated sample .cargo/config.toml file from the Zephyr build.
# At the time of generation, this represented the settings needed to allow
# a `cargo build` command to compile the rust code using the current Zephyr build.
# If any settings in the Zephyr build change, this could become out of date.
[build]
target = \"${RUST_TARGET}\"
target-dir = \"${CARGO_TARGET_DIR}\"

[env]
BUILD_DIR = \"${CMAKE_CURRENT_BINARY_DIR}\"
DOTCONFIG = \"${DOTCONFIG}\"
ZEPHYR_DTS = \"${ZEPHYR_DTS}\"
INCLUDE_DIRS = \"${include_dirs}\"
INCLUDE_DEFINES = \"${include_defines}\"
WRAPPER_FILE = \"${WRAPPER_FILE}\"
BINARY_DIR_INCLUDE_GENERATED = \"${BINARY_DIR_INCLUDE_GENERATED}\"

[patch.crates-io]
${config_paths}
")

# The library is built by invoking Cargo.
add_custom_command(
OUTPUT ${DUMMY_FILE}
BYPRODUCTS ${RUST_LIBRARY} ${WRAPPER_FILE}
COMMAND
${CMAKE_EXECUTABLE}
env BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR}
DOTCONFIG=${DOTCONFIG}
ZEPHYR_DTS=${ZEPHYR_DTS}
INCLUDE_DIRS="${include_dirs}"
INCLUDE_DEFINES="${include_defines}"
WRAPPER_FILE="${WRAPPER_FILE}"
BINARY_DIR_INCLUDE_GENERATED=${BINARY_DIR_INCLUDE_GENERATED}
cargo build
# TODO: release flag if release build
# --release

# Override the features according to the shield given. For a general case,
# this will need to come from a variable or argument.
# TODO: This needs to be passed in.
# --no-default-features
# --features ${SHIELD_FEATURE}

# Set a replacement so that packages can just use `zephyr-sys` as a package
# name to find it.
${command_paths}
--target ${RUST_TARGET}
--target-dir ${CARGO_TARGET_DIR}
COMMENT "Building Rust application"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

add_custom_target(librustapp ALL
DEPENDS ${DUMMY_FILE}
)

target_link_libraries(app PUBLIC -Wl,--allow-multiple-definition ${RUST_LIBRARY})
add_dependencies(app librustapp)

# Presumably, Rust applications will have no C source files, but cmake will require them.
# Add an empty file so that this will build. The main will come from the rust library.
target_sources(app PRIVATE ${ZEPHYR_BASE}/lib/rust/main.c ${WRAPPER_FILE})
set_source_files_properties(
${WRAPPER_FILE}
COMPILE_FLAGS "-I${ZEPHYR_BASE}/lib/rust/zephyr-sys"
)
endfunction()
1 change: 1 addition & 0 deletions cmake/modules/zephyr_default.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ list(APPEND zephyr_cmake_modules kconfig)
list(APPEND zephyr_cmake_modules arch_v2)
list(APPEND zephyr_cmake_modules soc_v1)
list(APPEND zephyr_cmake_modules soc_v2)
list(APPEND zephyr_cmake_modules rust)

foreach(component ${SUB_COMPONENTS})
if(NOT ${component} IN_LIST zephyr_cmake_modules)
Expand Down
2 changes: 1 addition & 1 deletion doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ message(STATUS "Zephyr base: ${ZEPHYR_BASE}")
#-------------------------------------------------------------------------------
# Options

set(SPHINXOPTS "-j auto -W --keep-going -T" CACHE STRING "Default Sphinx Options")
set(SPHINXOPTS "-j 4 -W --keep-going -T" CACHE STRING "Default Sphinx Options")
set(SPHINXOPTS_EXTRA "" CACHE STRING "Extra Sphinx Options (added to defaults)")
set(LATEXMKOPTS "-halt-on-error -no-shell-escape" CACHE STRING "Default latexmk options")
set(DT_TURBO_MODE OFF CACHE BOOL "Enable DT turbo mode")
Expand Down
1 change: 1 addition & 0 deletions doc/develop/languages/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Language Support

c/index.rst
cpp/index.rst
rust/index.rst
163 changes: 163 additions & 0 deletions doc/develop/languages/rust/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
.. _language_rust:

Rust Language Support
#####################

Rust is a systems programming language focused on safety, speed, and
concurrency. Designed to prevent common programming errors such as
null pointer dereferencing and buffer overflows, Rust emphasizes
memory safety without sacrificing performance. Its powerful type
system and ownership model ensure thread-safe programming, making it
an ideal choice for developing reliable and efficient low-level code.
Rust's expressive syntax and modern features make it a robust
alternative for developers working on embedded systems, operating
systems, and other performance-critical applications.

Enabling Rust Support
*********************

Currently, Zephyr supports applications written in Rust and C. The
enable Rust support, you must select the :kconfig:option:`CONFIG_RUST`
in the application configuration file.

The rust toolchain is separate from the rest of the Zephyr SDK. It
is recommended to use the `rustup`_ tool to install the rust
toolchain. In addition to the base compiler, you will need to install
core libraries for the target(s) you wish to compile on. It is
easiest to determine what needs to be installed by trying a build.
The diagnostics from the rust compilation will indicate the rust
command needed to install the appropriate target support:

.. _rustup: https://rustup.rs/

.. code-block::

$ west build ...
...
error[E0463]: can't find crate for `core`
|
= note: the `thumbv7m-none-eabi` target may not be installed
= help: consider downloading the target with `rustup target add thumb7m-none-eabi`

In this case, the given ``rustup`` command will install the needed
target support. The target needed will depend on both the board
selected, as well as certain configuration choices (such as whether
floating point is enabled).

Writing a Rust Application
**************************

See :zephyr_file:`samples/rust` for examples of an application.

CMake files
-----------

The application should contain a :file:`CMakeFiles.txt`, similar to
the following:

.. code-block:: cmake

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(hello_world)

rust_cargo_application()

Cargo files
-----------

Rust applications are built with Cargo. The Zephyr build system will
configure and invoke cargo in your application directory, with options
set so that it can find the Zephyr support libraries, and that the
output will be contained within the Zephyr build directory.

The :file:`Cargo.toml` will need to have a ``[lib]`` section that sets
``crate-type = ["staticlib"]``, and will need to include ``zephyr =
"0.1.0"`` as a dependency. You can use crates.io and the Crate
ecosystem to include any other dependencies you need. Just make sure
that you use crates that support building with no-std.

Application
-----------

The application source itself should live in file:`src/lib.rs`. A few
things are needed. A minimal file would be:

.. code-block:: rust

#![no_std]

extern crate zephyr;

#[no_mangle]
extern "C" fn rust_main() {
}

The ``no_std`` declaration is needed to prevent the code from
referencing the ``std`` library. The extern reference will cause the
zephyr crate to be brought in, even if nothing from it is used.
Practically, any meaningful Rust application on Zephyr will use
something from this crate, and this line is not necessary. Lastly,
the main declaration exports the main symbol so that it can be called
by C code. The build ``rust_cargo_application()`` cmake function will
include a small C file that will call into this from the C main
function.

Zephyr Functionality
********************

The bindings to Zephyr for Rust are under development, and are
currently rather minimalistic.

Bool Kconfig settings
---------------------

Boolean Kconfig settings can be used from within Rust code. Due to
design constraints by the Rust language, settings that affect
compilation must be determined before the build is made. In order to
use this in your application, you will need to use the
``zephyr-build`` crate, provided, to make these symbols available.

To your ``Cargo.toml`` file, add the following:

.. code-block:: toml

[build-dependencies]
zephyr-build = "0.1.0"

Then, you will need a ``build.rs`` file to call the support function.
The following will work:

.. code-block:: rust

fn main() {
zephyr_build::export_bool_kconfig();
}

At this point, it will be possible to use the ``cfg`` directive in
Rust on boolean Kconfig values. For example:

.. code-block:: rust

#[cfg(CONFIG_SCHED_DUMB)]
one_declaration;

#[cfg(not(CONFIG_SCHED_DUMB)]
other_declaration;

Other Kconfig settings
----------------------

All bool, numeric and string Kconfig settings are accessible from the
``zephyr::kconfig`` module. For example:

.. code-block:: rust

let ceiling = zephyr::kconfig::CONFIG_PRIORITY_CEILING - 1;

Other functionality
-------------------

Access to other functionality within zephyr is a work-in-progress, and
this document will be updated as that is done.
Loading
Loading