From a0611a1ea4de1b6f6ab8cf637a40f8eb6b5ae03f Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 12:35:23 -0600 Subject: [PATCH 01/35] rust: Initial config option Add the `CONFIG_RUST` Kconfig. This can be set to indicate that an application wishes to use Rust support. Signed-off-by: David Brown --- lib/Kconfig | 2 ++ lib/rust/Kconfig | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 lib/rust/Kconfig diff --git a/lib/Kconfig b/lib/Kconfig index af6717bc22ec..b2fccd6722b6 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -21,6 +21,8 @@ source "lib/posix/Kconfig" source "lib/open-amp/Kconfig" +source "lib/rust/Kconfig" + source "lib/smf/Kconfig" source "lib/acpi/Kconfig" diff --git a/lib/rust/Kconfig b/lib/rust/Kconfig new file mode 100644 index 000000000000..2cef924999d5 --- /dev/null +++ b/lib/rust/Kconfig @@ -0,0 +1,13 @@ +# Rust configuration options +# +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +menu "Rust Language Support" + +config RUST + bool "Rust support for the application" + help + This option enables the use of applications written in Rust. + +endmenu From 79eb8a2ff15acfdfb259ac40ccf921449eb8a244 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 12:36:50 -0600 Subject: [PATCH 02/35] rust: Simple main wrapper Until further variants are needed, this provides a `main()` function that simply calls into the `rust_main()` function. This is adequate for applications where main can be directly written in Rust. Signed-off-by: David Brown --- lib/rust/main.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 lib/rust/main.c diff --git a/lib/rust/main.c b/lib/rust/main.c new file mode 100644 index 000000000000..ccbc6948938b --- /dev/null +++ b/lib/rust/main.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 Linaro LTD + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This main is brought into the Rust application. */ + +#ifdef CONFIG_RUST + +extern void rust_main(); + +int main(void) +{ + rust_main(); + return 0; +} + +#endif From 6646068931d865de8c1cf9bb888036b844421683 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 13:04:52 -0600 Subject: [PATCH 03/35] rust: Basic `zephyr` crate The initial support crate for zephyr use within a Rust application. This crate does two simple things: - Processes the .config file generated for this build, and ensures that CONFIG_RUST is enabled. - Provide a hanging Rust panic handler. Signed-off-by: David Brown --- lib/rust/zephyr/Cargo.toml | 16 ++++++++++++++++ lib/rust/zephyr/build.rs | 36 ++++++++++++++++++++++++++++++++++++ lib/rust/zephyr/src/lib.rs | 22 ++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 lib/rust/zephyr/Cargo.toml create mode 100644 lib/rust/zephyr/build.rs create mode 100644 lib/rust/zephyr/src/lib.rs diff --git a/lib/rust/zephyr/Cargo.toml b/lib/rust/zephyr/Cargo.toml new file mode 100644 index 000000000000..622454931cf2 --- /dev/null +++ b/lib/rust/zephyr/Cargo.toml @@ -0,0 +1,16 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "zephyr" +version = "0.1.0" +edition = "2021" +description = """ +Functionality for Rust-based applications that run on Zephyr. +""" + +# These are needed at build time. +# Whether these need to be vendored is an open question. They are not +# used by the core Zephyr tree, but are needed by zephyr applications. +[build-dependencies] +regex = "1.10.3" diff --git a/lib/rust/zephyr/build.rs b/lib/rust/zephyr/build.rs new file mode 100644 index 000000000000..f05d84bbc6ad --- /dev/null +++ b/lib/rust/zephyr/build.rs @@ -0,0 +1,36 @@ +// Copyright (c) 2024 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +// Pre-build code for zephyr module. + +// This module makes the values from the generated .config available as conditional compilation. +// Note that this only applies to the zephyr module, and the user's application will not be able to +// see these definitions. To make that work, this will need to be moved into a support crate which +// can be invoked by the user's build.rs. + +// This builds a program that is run on the compilation host before the code is compiled. It can +// output configuration settings that affect the compilation. + +use std::io::{BufRead, BufReader}; +use std::env; +use std::fs::File; + +use regex::Regex; + +fn main() { + let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper"); + + // Ensure the build script is rerun when the dotconfig changes. + println!("cargo:rerun-if-env-changed=DOTCONFIG"); + println!("cargo-rerun-if-changed={}", dotconfig); + + let config_y = Regex::new(r"^(CONFIG_.*)=y$").unwrap(); + + let file = File::open(&dotconfig).expect("Unable to open dotconfig"); + for line in BufReader::new(file).lines() { + let line = line.expect("reading line from dotconfig"); + if let Some(caps) = config_y.captures(&line) { + println!("cargo:rustc-cfg={}", &caps[1]); + } + } +} diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs new file mode 100644 index 000000000000..d9970a98a0f5 --- /dev/null +++ b/lib/rust/zephyr/src/lib.rs @@ -0,0 +1,22 @@ +// Copyright (c) 2024 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +//! Zephyr application support for Rust +//! +//! This crates provides the core functionality for applications written in Rust that run on top of +//! Zephyr. + +#![no_std] + +// Ensure that Rust is enabled. +#[cfg(not(CONFIG_RUST))] +compile_error!("CONFIG_RUST must be set to build Rust in Zephyr"); + +use core::panic::PanicInfo; + +/// Override rust's panic. This simplistic initial version just hangs in a loop. +#[panic_handler] +fn panic(_ :&PanicInfo) -> ! { + loop { + } +} From 62af97a3c285f807adb70b09adb90efd98bcbe63 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 13:06:45 -0600 Subject: [PATCH 04/35] rust: Add cmake support for building rust apps This provides the function `rust_cargo_application()` within Cmake to give applications written in Rust a simple way to start. This implements the basic functionality needed to build a rust application on Arm, including target mapping, and invoking cargo during the build. Signed-off-by: David Brown --- cmake/modules/rust.cmake | 145 +++++++++++++++++++++++++++++ cmake/modules/zephyr_default.cmake | 1 + 2 files changed, 146 insertions(+) create mode 100644 cmake/modules/rust.cmake diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake new file mode 100644 index 000000000000..f8f29bad6b4a --- /dev/null +++ b/cmake/modules/rust.cmake @@ -0,0 +1,145 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Rust make support + +# Zephyr targets are defined through Kconfig. We need to map these to +# an appropriate llbm 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_ARMV7_M_FP) + set(RUST_TARGET "thumbv7em-none-eabihf" PARENT_SCOPE) + else() + set(RUST_TARGET "thumbv7m-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_ARMV7_M_FP) + 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() + else() + message(FATAL_ERROR "Rust: Add support for other target") + 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) + + _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") + + # 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}\" + +[patch.crates-io] +${config_paths} +") + + # The library is built by invoking Cargo. + add_custom_command( + OUTPUT ${DUMMY_FILE} + BYPRODUCTS ${RUST_LIBRARY} + COMMAND + ${CMAKE_EXECUTABLE} + env BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR} + DOTCONFIG=${DOTCONFIG} + ZEPHYR_DTS=${ZEPHYR_DTS} + 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) +endfunction() diff --git a/cmake/modules/zephyr_default.cmake b/cmake/modules/zephyr_default.cmake index 7472331255b8..e9e00b97ef5e 100644 --- a/cmake/modules/zephyr_default.cmake +++ b/cmake/modules/zephyr_default.cmake @@ -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) From 775a3a0ec2e5ac6ec5c7df3886fc96ff08ab3d46 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 13:10:52 -0600 Subject: [PATCH 05/35] rust: Simple rust hello_world application This implements a simple hello world application in Rust. Since there are no bindings yet, this just directly calls `printk` from Rust code. Signed-off-by: David Brown --- samples/rust/hello_world/CMakeLists.txt | 8 ++++++++ samples/rust/hello_world/Cargo.toml | 16 ++++++++++++++++ samples/rust/hello_world/prj.conf | 7 +++++++ samples/rust/hello_world/src/lib.rs | 19 +++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 samples/rust/hello_world/CMakeLists.txt create mode 100644 samples/rust/hello_world/Cargo.toml create mode 100644 samples/rust/hello_world/prj.conf create mode 100644 samples/rust/hello_world/src/lib.rs diff --git a/samples/rust/hello_world/CMakeLists.txt b/samples/rust/hello_world/CMakeLists.txt new file mode 100644 index 000000000000..dc45defc4651 --- /dev/null +++ b/samples/rust/hello_world/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(hello_world) + +rust_cargo_application() diff --git a/samples/rust/hello_world/Cargo.toml b/samples/rust/hello_world/Cargo.toml new file mode 100644 index 000000000000..4058e2d9a009 --- /dev/null +++ b/samples/rust/hello_world/Cargo.toml @@ -0,0 +1,16 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +[package] +# This must be rustapp for now. +name = "rustapp" +version = "0.1.0" +edition = "2021" +description = "A sample hello world application in Rust" +license = "Apache-2.0 or MIT" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +zephyr = "0.1.0" diff --git a/samples/rust/hello_world/prj.conf b/samples/rust/hello_world/prj.conf new file mode 100644 index 000000000000..093a1bb3ec4c --- /dev/null +++ b/samples/rust/hello_world/prj.conf @@ -0,0 +1,7 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_RUST=y + +CONFIG_RTT_CONSOLE=y +CONFIG_UART_CONSOLE=n diff --git a/samples/rust/hello_world/src/lib.rs b/samples/rust/hello_world/src/lib.rs new file mode 100644 index 000000000000..152cdb27c375 --- /dev/null +++ b/samples/rust/hello_world/src/lib.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2024 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +#![no_std] + +// Reference the Zephyr crate so that the panic handler gets used. This is only needed if no +// symbols from the crate are directly used. +extern crate zephyr; + +#[no_mangle] +extern "C" fn rust_main() { + unsafe { + printk("Hello world from Rust\n\0".as_ptr()); + } +} + +extern "C" { + fn printk(msg: *const u8); +} From 564d96d51d0baaf38f38bdd124e44abd19b78ab4 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 15:11:57 -0600 Subject: [PATCH 06/35] MAINTAINERS: Add Rust to maintainers file Indicate the new code is maintained. Signed-off-by: David Brown --- MAINTAINERS.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index 2d9f4d901bd8..7f412c5a1f4f 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -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: From 191eca3067a684a846b299e17757db1315dc2044 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 15:16:38 -0600 Subject: [PATCH 07/35] rust: Move Kconfig bool values into crate Create a crate `zephyr-build` that contains the code that will run at build time. Move the bool kconfig handing from this. This will make it easier for an application crate that needs access to config entries to do access them by: - Adding a dependency to zephyr-build to `[build-dependencies]` in their Cargo.toml. - Creating a build.rs, or adding a call to: zephyr_build::export_bool_kconfig() in it. Signed-off-by: David Brown --- cmake/modules/rust.cmake | 2 +- lib/rust/zephyr-build/Cargo.toml | 17 ++++++++++++++ lib/rust/zephyr-build/src/lib.rs | 38 ++++++++++++++++++++++++++++++++ lib/rust/zephyr/Cargo.toml | 2 +- lib/rust/zephyr/build.rs | 22 +----------------- 5 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 lib/rust/zephyr-build/Cargo.toml create mode 100644 lib/rust/zephyr-build/src/lib.rs diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index f8f29bad6b4a..d666f5e01533 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -43,7 +43,7 @@ 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) + set(LIB_RUST_CRATES zephyr zephyr-build) _rust_map_target() message(STATUS "Building Rust llvm target ${RUST_TARGET}") diff --git a/lib/rust/zephyr-build/Cargo.toml b/lib/rust/zephyr-build/Cargo.toml new file mode 100644 index 000000000000..c73a95e6e330 --- /dev/null +++ b/lib/rust/zephyr-build/Cargo.toml @@ -0,0 +1,17 @@ +# Copyright (c) 2024 Linaro LTD +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "zephyr-build" +version = "0.1.0" +edition = "2021" +description = """ +Build-time support for Rust-based applications that run on Zephyr. +Provides utilities for accessing Kconfig and devicetree information. +""" + +# These are needed at build time. +# Whether these need to be vendored is an open question. They are not +# used by the core Zephyr tree, but are needed by zephyr applications. +[dependencies] +regex = "1.10.3" diff --git a/lib/rust/zephyr-build/src/lib.rs b/lib/rust/zephyr-build/src/lib.rs new file mode 100644 index 000000000000..1a6ae71a061b --- /dev/null +++ b/lib/rust/zephyr-build/src/lib.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2024 Linaro LTD +// SPDX-License-Identifier: Apache-2.0 + +// Pre-build code for zephyr module. + +// This module makes the values from the generated .config available as conditional compilation. +// Note that this only applies to the zephyr module, and the user's application will not be able to +// see these definitions. To make that work, this will need to be moved into a support crate which +// can be invoked by the user's build.rs. + +// This builds a program that is run on the compilation host before the code is compiled. It can +// output configuration settings that affect the compilation. + +use std::io::{BufRead, BufReader}; +use std::env; +use std::fs::File; + +use regex::Regex; + +/// Export boolean Kconfig entries. This must happen in any crate that wishes to access the +/// configuration settings. +pub fn export_bool_kconfig() { + let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper"); + + // Ensure the build script is rerun when the dotconfig changes. + println!("cargo:rerun-if-env-changed=DOTCONFIG"); + println!("cargo-rerun-if-changed={}", dotconfig); + + let config_y = Regex::new(r"^(CONFIG_.*)=y$").unwrap(); + + let file = File::open(&dotconfig).expect("Unable to open dotconfig"); + for line in BufReader::new(file).lines() { + let line = line.expect("reading line from dotconfig"); + if let Some(caps) = config_y.captures(&line) { + println!("cargo:rustc-cfg={}", &caps[1]); + } + } +} diff --git a/lib/rust/zephyr/Cargo.toml b/lib/rust/zephyr/Cargo.toml index 622454931cf2..d5ba63aef7b9 100644 --- a/lib/rust/zephyr/Cargo.toml +++ b/lib/rust/zephyr/Cargo.toml @@ -13,4 +13,4 @@ Functionality for Rust-based applications that run on Zephyr. # Whether these need to be vendored is an open question. They are not # used by the core Zephyr tree, but are needed by zephyr applications. [build-dependencies] -regex = "1.10.3" +zephyr-build = "0.1.0" diff --git a/lib/rust/zephyr/build.rs b/lib/rust/zephyr/build.rs index f05d84bbc6ad..c3500ab0808c 100644 --- a/lib/rust/zephyr/build.rs +++ b/lib/rust/zephyr/build.rs @@ -11,26 +11,6 @@ // This builds a program that is run on the compilation host before the code is compiled. It can // output configuration settings that affect the compilation. -use std::io::{BufRead, BufReader}; -use std::env; -use std::fs::File; - -use regex::Regex; - fn main() { - let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper"); - - // Ensure the build script is rerun when the dotconfig changes. - println!("cargo:rerun-if-env-changed=DOTCONFIG"); - println!("cargo-rerun-if-changed={}", dotconfig); - - let config_y = Regex::new(r"^(CONFIG_.*)=y$").unwrap(); - - let file = File::open(&dotconfig).expect("Unable to open dotconfig"); - for line in BufReader::new(file).lines() { - let line = line.expect("reading line from dotconfig"); - if let Some(caps) = config_y.captures(&line) { - println!("cargo:rustc-cfg={}", &caps[1]); - } - } + zephyr_build::export_bool_kconfig(); } From 98f02913f16ec49104510f7a5d253e89d5089e10 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 15:43:37 -0600 Subject: [PATCH 08/35] rust: Make Kconfig values available to Rust code As part of the build of the `zephyr` crate, convert string and numeric entries from the .config file of the current build into constants. This mimics how these are available as defines in C code. The defines are available under `zephyr::kconfig::CONFIG_...`. Signed-off-by: David Brown --- lib/rust/zephyr-build/src/lib.rs | 36 +++++++++++++++++++++++++++++++- lib/rust/zephyr/build.rs | 1 + lib/rust/zephyr/src/lib.rs | 3 +++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/rust/zephyr-build/src/lib.rs b/lib/rust/zephyr-build/src/lib.rs index 1a6ae71a061b..6bbe615ded8a 100644 --- a/lib/rust/zephyr-build/src/lib.rs +++ b/lib/rust/zephyr-build/src/lib.rs @@ -11,9 +11,10 @@ // This builds a program that is run on the compilation host before the code is compiled. It can // output configuration settings that affect the compilation. -use std::io::{BufRead, BufReader}; +use std::io::{BufRead, BufReader, Write}; use std::env; use std::fs::File; +use std::path::Path; use regex::Regex; @@ -36,3 +37,36 @@ pub fn export_bool_kconfig() { } } } + +/// Capture bool, numeric and string kconfig values in a 'kconfig' module. +/// This is a little simplistic, and will make the entries numeric if they look like numbers. +/// Ideally, this would be built on the types of the values, but that will require more +/// introspection. +pub fn build_kconfig_mod() { + let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper"); + let outdir = env::var("OUT_DIR").expect("OUT_DIR must be set"); + + let config_num = Regex::new(r"^(CONFIG_.*)=([1-9][0-9]*|0x[0-9a-fA-F]+)$").unwrap(); + // It is unclear what quoting might be used in the .config. + let config_str = Regex::new(r#"^(CONFIG_.*)=(".*")$"#).unwrap(); + let gen_path = Path::new(&outdir).join("kconfig.rs"); + + let mut f = File::create(&gen_path).unwrap(); + writeln!(&mut f, "pub mod kconfig {{").unwrap(); + + let file = File::open(&dotconfig).expect("Unable to open dotconfig"); + for line in BufReader::new(file).lines() { + let line = line.expect("reading line from dotconfig"); + if let Some(caps) = config_num.captures(&line) { + writeln!(&mut f, " #[allow(dead_code)]").unwrap(); + writeln!(&mut f, " pub const {}: usize = {};", + &caps[1], &caps[2]).unwrap(); + } + if let Some(caps) = config_str.captures(&line) { + writeln!(&mut f, " #[allow(dead_code)]").unwrap(); + writeln!(&mut f, " pub const {}: &'static str = {};", + &caps[1], &caps[2]).unwrap(); + } + } + writeln!(&mut f, "}}").unwrap(); +} diff --git a/lib/rust/zephyr/build.rs b/lib/rust/zephyr/build.rs index c3500ab0808c..f4345e95cc8c 100644 --- a/lib/rust/zephyr/build.rs +++ b/lib/rust/zephyr/build.rs @@ -13,4 +13,5 @@ fn main() { zephyr_build::export_bool_kconfig(); + zephyr_build::build_kconfig_mod(); } diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index d9970a98a0f5..62ce33d841ee 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -8,6 +8,9 @@ #![no_std] +// Bring in the generated kconfig module +include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); + // Ensure that Rust is enabled. #[cfg(not(CONFIG_RUST))] compile_error!("CONFIG_RUST must be set to build Rust in Zephyr"); From 62770bf8f8131790adf4895fcc25b2d8da868f65 Mon Sep 17 00:00:00 2001 From: David Brown Date: Mon, 15 Jul 2024 15:44:54 -0600 Subject: [PATCH 09/35] samples: rust: Access CONFIG_BOARD This is a bit awkward, as we don't have a clean interface between Rust and the Zephyr C code, but copy and null-terminate the current board, and include that in the printed message. This demonstrates that the kconfig module generation is working. Signed-off-by: David Brown --- samples/rust/hello_world/src/lib.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/samples/rust/hello_world/src/lib.rs b/samples/rust/hello_world/src/lib.rs index 152cdb27c375..ba50dd1f663e 100644 --- a/samples/rust/hello_world/src/lib.rs +++ b/samples/rust/hello_world/src/lib.rs @@ -9,11 +9,18 @@ extern crate zephyr; #[no_mangle] extern "C" fn rust_main() { + // Until we have allocation, converting a Rust string into a C string is a bit awkward. + // This works because the kconfig values are const. + const BOARD_SRC: &'static str = zephyr::kconfig::CONFIG_BOARD; + let mut board = [0u8; BOARD_SRC.len() + 1]; + board[..BOARD_SRC.len()].clone_from_slice(BOARD_SRC.as_bytes()); + board[BOARD_SRC.len()] = 0; + unsafe { - printk("Hello world from Rust\n\0".as_ptr()); + printk("Hello world from Rust on %s\n\0".as_ptr(), board.as_ptr()); } } extern "C" { - fn printk(msg: *const u8); + fn printk(msg: *const u8, arg1: *const u8); } From 6f55e187394a8a33dd990b3d58ad5a7d5b65f3d5 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 16 Jul 2024 15:40:17 -0600 Subject: [PATCH 10/35] doc: languages: Add Rust documentation Add initial docs for Rust language support. Explains the support currently present, how to install the toolchain needed, and explains what is in the current sample. Signed-off-by: David Brown --- doc/develop/languages/index.rst | 1 + doc/develop/languages/rust/index.rst | 163 +++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 doc/develop/languages/rust/index.rst diff --git a/doc/develop/languages/index.rst b/doc/develop/languages/index.rst index 033b9084ac4f..b1b54355f1a4 100644 --- a/doc/develop/languages/index.rst +++ b/doc/develop/languages/index.rst @@ -8,3 +8,4 @@ Language Support c/index.rst cpp/index.rst + rust/index.rst diff --git a/doc/develop/languages/rust/index.rst b/doc/develop/languages/rust/index.rst new file mode 100644 index 000000000000..a986ff576019 --- /dev/null +++ b/doc/develop/languages/rust/index.rst @@ -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:: bash + + $ 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_MY_SETTING)] + one_declaration; + + #[cfg(not(CONFIG_MY_SETTING)] + 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 ram_size = zephyr::kconfig::CONFIG_RAM_SIZE * 1024; + +Other functionality +------------------- + +Access to other functionality within zephyr is a work-in-progress, and +this document will be updated as that is done. From cf88e6fabb5a51ade3bfb28059df077c67071fe0 Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 16 Jul 2024 17:23:13 -0600 Subject: [PATCH 11/35] samples: rust: Rename hello The documentation system requires all of the samples to have unique names, no matter what directory they are in. Rename our sample to `hello_rust` to avoid a conflict with the existing `hello_world` sample. Signed-off-by: David Brown --- samples/rust/{hello_world => hello_rust}/CMakeLists.txt | 0 samples/rust/{hello_world => hello_rust}/Cargo.toml | 0 samples/rust/{hello_world => hello_rust}/prj.conf | 0 samples/rust/{hello_world => hello_rust}/src/lib.rs | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename samples/rust/{hello_world => hello_rust}/CMakeLists.txt (100%) rename samples/rust/{hello_world => hello_rust}/Cargo.toml (100%) rename samples/rust/{hello_world => hello_rust}/prj.conf (100%) rename samples/rust/{hello_world => hello_rust}/src/lib.rs (100%) diff --git a/samples/rust/hello_world/CMakeLists.txt b/samples/rust/hello_rust/CMakeLists.txt similarity index 100% rename from samples/rust/hello_world/CMakeLists.txt rename to samples/rust/hello_rust/CMakeLists.txt diff --git a/samples/rust/hello_world/Cargo.toml b/samples/rust/hello_rust/Cargo.toml similarity index 100% rename from samples/rust/hello_world/Cargo.toml rename to samples/rust/hello_rust/Cargo.toml diff --git a/samples/rust/hello_world/prj.conf b/samples/rust/hello_rust/prj.conf similarity index 100% rename from samples/rust/hello_world/prj.conf rename to samples/rust/hello_rust/prj.conf diff --git a/samples/rust/hello_world/src/lib.rs b/samples/rust/hello_rust/src/lib.rs similarity index 100% rename from samples/rust/hello_world/src/lib.rs rename to samples/rust/hello_rust/src/lib.rs From 6052ca479354fb453cf6e6ad4917c20cfff78a9e Mon Sep 17 00:00:00 2001 From: David Brown Date: Tue, 16 Jul 2024 17:56:45 -0600 Subject: [PATCH 12/35] doc: Use proper quote block for shell Code-blocks for shell commands are of type 'shell', to avoid this warning. Signed-off-by: David Brown --- doc/develop/languages/rust/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/develop/languages/rust/index.rst b/doc/develop/languages/rust/index.rst index a986ff576019..76ea495489c1 100644 --- a/doc/develop/languages/rust/index.rst +++ b/doc/develop/languages/rust/index.rst @@ -30,11 +30,11 @@ command needed to install the appropriate target support: .. _rustup: https://rustup.rs/ -.. code-block:: bash +.. code-block:: shell $ west build ... ... - error[E0463]: can't find crate for `core` + error[E0463]: cant 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` From c95dc4313888cbc3d139011b45382fd59e4f9dc0 Mon Sep 17 00:00:00 2001 From: David Brown Date: Wed, 17 Jul 2024 11:05:35 -0600 Subject: [PATCH 13/35] rust: Support negative Kconfig int values Distinguish between 'int' and 'hex' Kconfig values. Use `usize` for the hex values, as these are unsigned and may have the high bit set. But, for 'int' values, use `isize`, and allow the constants to be negative. Signed-off-by: David Brown --- lib/rust/zephyr-build/src/lib.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/rust/zephyr-build/src/lib.rs b/lib/rust/zephyr-build/src/lib.rs index 6bbe615ded8a..5ba759571ae3 100644 --- a/lib/rust/zephyr-build/src/lib.rs +++ b/lib/rust/zephyr-build/src/lib.rs @@ -46,7 +46,9 @@ pub fn build_kconfig_mod() { let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper"); let outdir = env::var("OUT_DIR").expect("OUT_DIR must be set"); - let config_num = Regex::new(r"^(CONFIG_.*)=([1-9][0-9]*|0x[0-9a-fA-F]+)$").unwrap(); + // The assumption is that hex values are unsigned, and decimal are signed. + let config_hex = Regex::new(r"^(CONFIG_.*)=(0x[0-9a-fA-F]+)$").unwrap(); + let config_int = Regex::new(r"^(CONFIG_.*)=(-?[1-9][0-9]*)$").unwrap(); // It is unclear what quoting might be used in the .config. let config_str = Regex::new(r#"^(CONFIG_.*)=(".*")$"#).unwrap(); let gen_path = Path::new(&outdir).join("kconfig.rs"); @@ -57,11 +59,16 @@ pub fn build_kconfig_mod() { let file = File::open(&dotconfig).expect("Unable to open dotconfig"); for line in BufReader::new(file).lines() { let line = line.expect("reading line from dotconfig"); - if let Some(caps) = config_num.captures(&line) { + if let Some(caps) = config_hex.captures(&line) { writeln!(&mut f, " #[allow(dead_code)]").unwrap(); writeln!(&mut f, " pub const {}: usize = {};", &caps[1], &caps[2]).unwrap(); } + if let Some(caps) = config_int.captures(&line) { + writeln!(&mut f, " #[allow(dead_code)]").unwrap(); + writeln!(&mut f, " pub const {}: isize = {};", + &caps[1], &caps[2]).unwrap(); + } if let Some(caps) = config_str.captures(&line) { writeln!(&mut f, " #[allow(dead_code)]").unwrap(); writeln!(&mut f, " pub const {}: &'static str = {};", From db90a844656ec2fc06b392ac322218c0525e6751 Mon Sep 17 00:00:00 2001 From: David Brown Date: Wed, 17 Jul 2024 11:09:19 -0600 Subject: [PATCH 14/35] rust: Mark RUST support as experimental This is still very experimental, mark it in the Kconfig as such. Signed-off-by: David Brown --- lib/rust/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rust/Kconfig b/lib/rust/Kconfig index 2cef924999d5..c9b7b5eb68e3 100644 --- a/lib/rust/Kconfig +++ b/lib/rust/Kconfig @@ -7,6 +7,7 @@ menu "Rust Language Support" config RUST bool "Rust support for the application" + select EXPERIMENTAL help This option enables the use of applications written in Rust. From 285c9d5d5997a8869e56c278d639cbc5e24aa695 Mon Sep 17 00:00:00 2001 From: David Brown Date: Thu, 18 Jul 2024 08:13:22 -0600 Subject: [PATCH 15/35] cmake: rust: Use proper FPU config For Arm targets, match the ABI selection defines from the cmake/compiler/gcc/target_arm.cmake file. What is important here is that the floating point selection is the same between gcc and rustc. Signed-off-by: David Brown --- cmake/modules/rust.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index d666f5e01533..c96c4ad6e1aa 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -14,7 +14,7 @@ function(_rust_map_target) elseif(CONFIG_CPU_CORTEX_M3) set(RUST_TARGET "thumbv7m-none-eabi" PARENT_SCOPE) elseif(CONFIG_CPU_CORTEX_M4) - if(CONFIG_ARMV7_M_FP) + if(CONFIG_FP_HARDABI OR FORCE_FP_HARDABI) set(RUST_TARGET "thumbv7em-none-eabihf" PARENT_SCOPE) else() set(RUST_TARGET "thumbv7m-none-eabi" PARENT_SCOPE) @@ -23,7 +23,7 @@ function(_rust_map_target) 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_ARMV7_M_FP) + 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) From a8d1745565ade1a0c718b1ce9a8ecefa33e231dc Mon Sep 17 00:00:00 2001 From: David Brown Date: Thu, 18 Jul 2024 08:28:32 -0600 Subject: [PATCH 16/35] doc: rust: Fix code block Because of a mismatched quote, shell quoting for this block fails sphinx. Removing the quite causes compliance to fail due to the mispelling. Fix this by just removing formatting entirely from the block. Signed-off-by: David Brown --- doc/develop/languages/rust/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/develop/languages/rust/index.rst b/doc/develop/languages/rust/index.rst index 76ea495489c1..04f466946c89 100644 --- a/doc/develop/languages/rust/index.rst +++ b/doc/develop/languages/rust/index.rst @@ -30,11 +30,11 @@ command needed to install the appropriate target support: .. _rustup: https://rustup.rs/ -.. code-block:: shell +.. code-block:: $ west build ... ... - error[E0463]: cant find crate for `core` + 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` From 2f392721cc098910f3a8764e083f359b40746566 Mon Sep 17 00:00:00 2001 From: David Brown Date: Thu, 18 Jul 2024 08:29:24 -0600 Subject: [PATCH 17/35] doc: rust: Use real config settings Compliance fails if even an example refers to non-existent Kconfig options. Change these to real options, even though that is slightly distracting. Signed-off-by: David Brown --- doc/develop/languages/rust/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/develop/languages/rust/index.rst b/doc/develop/languages/rust/index.rst index 04f466946c89..c7e8b586dc6c 100644 --- a/doc/develop/languages/rust/index.rst +++ b/doc/develop/languages/rust/index.rst @@ -140,10 +140,10 @@ Rust on boolean Kconfig values. For example: .. code-block:: rust - #[cfg(CONFIG_MY_SETTING)] + #[cfg(CONFIG_SCHED_DUMB)] one_declaration; - #[cfg(not(CONFIG_MY_SETTING)] + #[cfg(not(CONFIG_SCHED_DUMB)] other_declaration; Other Kconfig settings @@ -154,7 +154,7 @@ All bool, numeric and string Kconfig settings are accessible from the .. code-block:: rust - let ram_size = zephyr::kconfig::CONFIG_RAM_SIZE * 1024; + let ceiling = zephyr::kconfig::CONFIG_PRIORITY_CEILING - 1; Other functionality ------------------- From f883380cdaad6cd978cf669523d8f02980231b60 Mon Sep 17 00:00:00 2001 From: David Brown Date: Thu, 18 Jul 2024 09:11:57 -0600 Subject: [PATCH 18/35] cmake: rust: Fix Cortex-M4 non-fp target The Cortex-M4 should be the thumbv7em not thumbv7m. Signed-off-by: David Brown --- cmake/modules/rust.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index c96c4ad6e1aa..9fda57d490a4 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -17,7 +17,7 @@ function(_rust_map_target) if(CONFIG_FP_HARDABI OR FORCE_FP_HARDABI) set(RUST_TARGET "thumbv7em-none-eabihf" PARENT_SCOPE) else() - set(RUST_TARGET "thumbv7m-none-eabi" PARENT_SCOPE) + set(RUST_TARGET "thumbv7em-none-eabi" PARENT_SCOPE) endif() elseif(CONFIG_CPU_CORTEX_M23) set(RUST_TARGET "thumbv8m.base-none-eabi" PARENT_SCOPE) From b72fdeb4a67b38ca188f2351d6d9038305ad4a18 Mon Sep 17 00:00:00 2001 From: David Brown Date: Thu, 18 Jul 2024 12:47:13 -0600 Subject: [PATCH 19/35] rust: Fix prototype for rust_main Make this a proper C prototype function that takes no arguments. Signed-off-by: David Brown --- lib/rust/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rust/main.c b/lib/rust/main.c index ccbc6948938b..da6fc5548f8e 100644 --- a/lib/rust/main.c +++ b/lib/rust/main.c @@ -7,7 +7,7 @@ #ifdef CONFIG_RUST -extern void rust_main(); +extern void rust_main(void); int main(void) { From 157448eadf19ac158180a911541dfccb5859fbc1 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Tue, 16 Jul 2024 13:43:12 +0200 Subject: [PATCH 20/35] remove board specific options --- samples/rust/hello_rust/prj.conf | 3 --- 1 file changed, 3 deletions(-) diff --git a/samples/rust/hello_rust/prj.conf b/samples/rust/hello_rust/prj.conf index 093a1bb3ec4c..1251e5ba23e9 100644 --- a/samples/rust/hello_rust/prj.conf +++ b/samples/rust/hello_rust/prj.conf @@ -2,6 +2,3 @@ # SPDX-License-Identifier: Apache-2.0 CONFIG_RUST=y - -CONFIG_RTT_CONSOLE=y -CONFIG_UART_CONSOLE=n From 98d0bf7d4f1701ae8bc6b61b0b439846a64baf1e Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Tue, 16 Jul 2024 13:43:31 +0200 Subject: [PATCH 21/35] target mapping for cortex m7 --- cmake/modules/rust.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index 9fda57d490a4..d5dddbdd132b 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -13,7 +13,7 @@ function(_rust_map_target) 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) + elseif(CONFIG_CPU_CORTEX_M4 OR CONFIG_CPU_CORTEX_M7) if(CONFIG_FP_HARDABI OR FORCE_FP_HARDABI) set(RUST_TARGET "thumbv7em-none-eabihf" PARENT_SCOPE) else() From 680250e0b50280302a2567d1e8dc1a31797a9ae6 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Wed, 17 Jul 2024 16:29:44 +0200 Subject: [PATCH 22/35] first trial of providing dts information to rust --- cmake/modules/dts.cmake | 21 +++++++ scripts/dts/gen_dts_rust.py | 110 ++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100755 scripts/dts/gen_dts_rust.py diff --git a/cmake/modules/dts.cmake b/cmake/modules/dts.cmake index ece7be588a31..15559a04a733 100644 --- a/cmake/modules/dts.cmake +++ b/cmake/modules/dts.cmake @@ -118,6 +118,11 @@ set(GEN_DTS_CMAKE_SCRIPT ${DT_SCRIPTS}/gen_dts_cmake.py) # creating it. set(DTS_CMAKE ${PROJECT_BINARY_DIR}/dts.cmake) +# This generates DT information needed by Rust applications. +set(GEN_DTS_RUST_SCRIPT ${DT_SCRIPTS}/gen_dts_rust.py) +# The generated information itself, which is further processed by the Rust build script. +set(DTS_RUST ${PROJECT_BINARY_DIR}/dts.rs) + # The location of a file containing known vendor prefixes, relative to # each element of DTS_ROOT. Users can define their own in their own # modules. @@ -269,6 +274,7 @@ set_property(DIRECTORY APPEND PROPERTY ${GEN_DEFINES_SCRIPT} ${GEN_DRIVER_KCONFIG_SCRIPT} ${GEN_DTS_CMAKE_SCRIPT} + ${GEN_DTS_RUST_SCRIPT} ) # @@ -335,6 +341,21 @@ else() include(${DTS_CMAKE}) endif() +# +# Run GEN_DTS_RUST_SCRIPT. +# + +execute_process( + COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_RUST_SCRIPT} + --edt-pickle ${EDT_PICKLE} + --rust-out ${DTS_RUST} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + RESULT_VARIABLE ret +) +if(NOT "${ret}" STREQUAL "0") + message(FATAL_ERROR "gen_dts_rust.py failed with return code: ${ret}") +endif() + # # Run dtc if it was found. # diff --git a/scripts/dts/gen_dts_rust.py b/scripts/dts/gen_dts_rust.py new file mode 100755 index 000000000000..f64fd4d5aa7f --- /dev/null +++ b/scripts/dts/gen_dts_rust.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 + +import re +import os +import sys +import pickle +import argparse + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python-devicetree', + 'src')) + + +def parse_args(): + parser = argparse.ArgumentParser(allow_abbrev=False) + parser.add_argument("--rust-out", required=True, + help="path to write the Rust device tree file") + parser.add_argument("--edt-pickle", required=True, + help="path to read the pickled edtlib.EDT object from") + return parser.parse_args() + + +def main(): + args = parse_args() + + with open(args.edt_pickle, 'rb') as f: + edt = pickle.load(f) + + with open(args.rust_out, 'w') as f: + f.write(''.join(generate_dts_rust(edt))) + + +def generate_dts_rust(edt): + for node in edt.nodes: + yield from generate_node_struct(node) + + for node in edt.nodes: + yield from generate_node_instance(node) + + +def generate_node_struct(node): + yield f'struct DtNode{node.dep_ordinal} {{\n' + + for child_name, child_node in node.children.items(): + yield f' {replace_chars(child_name)}: &DtNode{child_node.dep_ordinal},\n' + + for property_name, property_object in node.props.items(): + rust_type = None + + if property_object.spec.type == 'boolean': + rust_type = 'bool' + if property_object.spec.type == 'int': + rust_type = 'u32' + if property_object.spec.type == 'string': + rust_type = 'str' + if property_object.spec.type == 'array': + rust_type = '[u32]' + if property_object.spec.type == 'string-array': + rust_type = '[str]' + if property_object.spec.type == 'phandle' or property_object.spec.type == 'path': + rust_type = f'&DtNode{property_object.val.dep_ordinal}' + if property_object.spec.type == 'phandle-array': + pass + + if rust_type: + yield f' {replace_chars(property_name)}: {rust_type},\n' + else: + yield f' // {replace_chars(property_name)} ({property_object.spec.type})\n' + + + yield '}\n\n' + + +def generate_node_instance(node): + for label in node.labels: + yield f'// label("{label}")\n' + if node.matching_compat: + yield f'// matching_compat("{node.matching_compat}")\n' + + yield f'const DT_NODE_{node.dep_ordinal}: DtNode{node.dep_ordinal} = DtNode{node.dep_ordinal} {{\n' + + for child_name, child_node in node.children.items(): + yield f' {replace_chars(child_name)}: DT_NODE_{child_node.dep_ordinal},\n' + + for property_name, property_object in node.props.items(): + if property_object.spec.type == 'boolean': + yield f' {replace_chars(property_name)}: {"true" if property_object.val else "false"},\n' + if property_object.spec.type == 'int': + yield f' {replace_chars(property_name)}: {property_object.val},\n' + if property_object.spec.type == 'string': + yield f' {replace_chars(property_name)}: "{property_object.val}",\n' + if property_object.spec.type == 'array': + yield f' {replace_chars(property_name)}: [' + yield ', '.join(str(val) for val in property_object.val) + yield '],\n' + if property_object.spec.type == 'string-array': + yield f' {replace_chars(property_name)}: [' + yield ', '.join(f'"{val}"' for val in property_object.val) + yield '],\n' + if property_object.spec.type == 'phandle' or property_object.spec.type == 'path': + yield f' {replace_chars(property_name)}: "DT_NODE_{property_object.val.dep_ordinal}",\n' + + yield '};\n\n' + + +def replace_chars(text: str): + return re.sub(r'\W+', '_', text) + + +if __name__ == "__main__": + main() From e3abd1c601d32291c67ae587d4504ddc8e8a065d Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Thu, 18 Jul 2024 09:52:11 +0200 Subject: [PATCH 23/35] add zephyr-sys library with bindgen --- cmake/modules/rust.cmake | 2 +- lib/rust/zephyr-sys/Cargo.toml | 9 +++++++++ lib/rust/zephyr-sys/build.rs | 21 +++++++++++++++++++++ lib/rust/zephyr-sys/src/lib.rs | 5 +++++ lib/rust/zephyr-sys/wrapper.h | 1 + lib/rust/zephyr/Cargo.toml | 1 + 6 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 lib/rust/zephyr-sys/Cargo.toml create mode 100644 lib/rust/zephyr-sys/build.rs create mode 100644 lib/rust/zephyr-sys/src/lib.rs create mode 100644 lib/rust/zephyr-sys/wrapper.h diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index d5dddbdd132b..b2d7c22bb21a 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -43,7 +43,7 @@ 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-build) + set(LIB_RUST_CRATES zephyr zephyr-build zephyr-sys) _rust_map_target() message(STATUS "Building Rust llvm target ${RUST_TARGET}") diff --git a/lib/rust/zephyr-sys/Cargo.toml b/lib/rust/zephyr-sys/Cargo.toml new file mode 100644 index 000000000000..9dae6209ccb3 --- /dev/null +++ b/lib/rust/zephyr-sys/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "zephyr-sys" +version = "0.1.0" +edition = "2021" + +[dependencies] + +[build-dependencies] +bindgen = { version = "0.69.4", features = ["experimental"] } diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs new file mode 100644 index 000000000000..297039b35f0e --- /dev/null +++ b/lib/rust/zephyr-sys/build.rs @@ -0,0 +1,21 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + let bindings = bindgen::Builder::default() + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/include") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated") + .clang_arg("-imacros/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated/zephyr/autoconf.h") + .wrap_static_fns(true) + .allowlist_function("gpio_.*") + .header("wrapper.h") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/lib/rust/zephyr-sys/src/lib.rs b/lib/rust/zephyr-sys/src/lib.rs new file mode 100644 index 000000000000..a38a13a81db4 --- /dev/null +++ b/lib/rust/zephyr-sys/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/lib/rust/zephyr-sys/wrapper.h b/lib/rust/zephyr-sys/wrapper.h new file mode 100644 index 000000000000..e60380b8278d --- /dev/null +++ b/lib/rust/zephyr-sys/wrapper.h @@ -0,0 +1 @@ +#include diff --git a/lib/rust/zephyr/Cargo.toml b/lib/rust/zephyr/Cargo.toml index d5ba63aef7b9..8d781f2f8c82 100644 --- a/lib/rust/zephyr/Cargo.toml +++ b/lib/rust/zephyr/Cargo.toml @@ -14,3 +14,4 @@ Functionality for Rust-based applications that run on Zephyr. # used by the core Zephyr tree, but are needed by zephyr applications. [build-dependencies] zephyr-build = "0.1.0" +zephyr-sys = "0.1.0" From 05652a740719d55279222800cf2074ff09cbc713 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Thu, 18 Jul 2024 13:12:03 +0200 Subject: [PATCH 24/35] control gpio from rust --- cmake/modules/rust.cmake | 4 ++ lib/rust/main.c | 19 ++++++++++ lib/rust/zephyr-sys/build.rs | 5 +++ lib/rust/zephyr-sys/src/lib.rs | 6 +++ lib/rust/zephyr/Cargo.toml | 4 +- lib/rust/zephyr/src/gpio.rs | 22 +++++++++++ lib/rust/zephyr/src/lib.rs | 60 ++++++++++++++++++++++++++++++ samples/rust/hello_rust/src/lib.rs | 7 ++++ 8 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 lib/rust/zephyr/src/gpio.rs diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index b2d7c22bb21a..b08e972b1b72 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -142,4 +142,8 @@ ${config_paths} # 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) + + # TODO: Make safer and nicer + target_sources(app PRIVATE /tmp/bindgen/extern.c) + target_include_directories(app PRIVATE ${ZEPHYR_BASE}/lib/rust/zephyr-sys) endfunction() diff --git a/lib/rust/main.c b/lib/rust/main.c index da6fc5548f8e..cb786c7306ad 100644 --- a/lib/rust/main.c +++ b/lib/rust/main.c @@ -7,12 +7,31 @@ #ifdef CONFIG_RUST +#include +#include + extern void rust_main(void); +const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); + int main(void) { + if (!gpio_is_ready_dt(&led)) { + return 0; + } + + int ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); + if (ret < 0) { + return 0; + } + rust_main(); return 0; } +void sample_sleep() +{ + k_msleep(1000); +} + #endif diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs index 297039b35f0e..bdb60799f2de 100644 --- a/lib/rust/zephyr-sys/build.rs +++ b/lib/rust/zephyr-sys/build.rs @@ -3,6 +3,11 @@ use std::path::PathBuf; fn main() { let bindings = bindgen::Builder::default() + .use_core() + // TODO: Fix include paths + .clang_arg("-nostdinc") + .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/12.2.0/include") + .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/picolibc/include") .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/include") .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated") .clang_arg("-imacros/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated/zephyr/autoconf.h") diff --git a/lib/rust/zephyr-sys/src/lib.rs b/lib/rust/zephyr-sys/src/lib.rs index a38a13a81db4..d63d629ce07a 100644 --- a/lib/rust/zephyr-sys/src/lib.rs +++ b/lib/rust/zephyr-sys/src/lib.rs @@ -1,5 +1,11 @@ +#![no_std] + #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +use core::include; +use core::concat; +use core::env; + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/lib/rust/zephyr/Cargo.toml b/lib/rust/zephyr/Cargo.toml index 8d781f2f8c82..02f73d1dd1fb 100644 --- a/lib/rust/zephyr/Cargo.toml +++ b/lib/rust/zephyr/Cargo.toml @@ -9,9 +9,11 @@ description = """ Functionality for Rust-based applications that run on Zephyr. """ +[dependencies] +zephyr-sys = "0.1.0" + # These are needed at build time. # Whether these need to be vendored is an open question. They are not # used by the core Zephyr tree, but are needed by zephyr applications. [build-dependencies] zephyr-build = "0.1.0" -zephyr-sys = "0.1.0" diff --git a/lib/rust/zephyr/src/gpio.rs b/lib/rust/zephyr/src/gpio.rs new file mode 100644 index 000000000000..1a80b8cd1250 --- /dev/null +++ b/lib/rust/zephyr/src/gpio.rs @@ -0,0 +1,22 @@ +use zephyr_sys::{gpio_dt_spec, gpio_pin_toggle_dt}; + +extern "C" { + static led: gpio_dt_spec; +} + +extern "C" { + fn sample_sleep(); +} + +pub fn toggle() { + unsafe { + gpio_pin_toggle_dt(&led); + } +} + +pub fn sleep() { + unsafe { + sample_sleep(); + } +} + diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 62ce33d841ee..0c08000bbd2f 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -8,6 +8,10 @@ #![no_std] +extern crate alloc; + +pub mod gpio; + // Bring in the generated kconfig module include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); @@ -15,7 +19,12 @@ include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); #[cfg(not(CONFIG_RUST))] compile_error!("CONFIG_RUST must be set to build Rust in Zephyr"); +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::UnsafeCell; use core::panic::PanicInfo; +use core::ptr::null_mut; +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering::Relaxed; /// Override rust's panic. This simplistic initial version just hangs in a loop. #[panic_handler] @@ -23,3 +32,54 @@ fn panic(_ :&PanicInfo) -> ! { loop { } } + +const ARENA_SIZE: usize = 32 * 1024; +const MAX_SUPPORTED_ALIGN: usize = 4096; + +#[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN +struct SimpleAllocator { + arena: UnsafeCell<[u8; ARENA_SIZE]>, + remaining: AtomicUsize, // we allocate from the top, counting down +} + +#[global_allocator] +static ALLOCATOR: SimpleAllocator = SimpleAllocator { + arena: UnsafeCell::new([0x55; ARENA_SIZE]), + remaining: AtomicUsize::new(ARENA_SIZE), +}; + +unsafe impl Sync for SimpleAllocator {} + +unsafe impl GlobalAlloc for SimpleAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + + // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2. + // So we can safely use a mask to ensure alignment without worrying about UB. + let align_mask_to_round_down = !(align - 1); + + if align > MAX_SUPPORTED_ALIGN { + return null_mut(); + } + + let mut allocated = 0; + if self + .remaining + .fetch_update(Relaxed, Relaxed, |mut remaining| { + if size > remaining { + return None; + } + remaining -= size; + remaining &= align_mask_to_round_down; + allocated = remaining; + Some(remaining) + }) + .is_err() + { + return null_mut(); + }; + self.arena.get().cast::().add(allocated) + } + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} \ No newline at end of file diff --git a/samples/rust/hello_rust/src/lib.rs b/samples/rust/hello_rust/src/lib.rs index ba50dd1f663e..355d0bfcd465 100644 --- a/samples/rust/hello_rust/src/lib.rs +++ b/samples/rust/hello_rust/src/lib.rs @@ -7,6 +7,8 @@ // symbols from the crate are directly used. extern crate zephyr; +use zephyr::gpio::{sleep, toggle}; + #[no_mangle] extern "C" fn rust_main() { // Until we have allocation, converting a Rust string into a C string is a bit awkward. @@ -19,6 +21,11 @@ extern "C" fn rust_main() { unsafe { printk("Hello world from Rust on %s\n\0".as_ptr(), board.as_ptr()); } + + loop { + toggle(); + sleep(); + } } extern "C" { From 96f4e06fb7783df4cf0ad4b1990f9821057b3f96 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Thu, 18 Jul 2024 13:47:49 +0200 Subject: [PATCH 25/35] call sleep directly --- lib/rust/main.c | 5 ----- lib/rust/zephyr-sys/build.rs | 17 +++++++++++++++++ lib/rust/zephyr-sys/src/lib.rs | 1 + lib/rust/zephyr-sys/wrapper.h | 1 + lib/rust/zephyr/src/gpio.rs | 8 ++------ 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/rust/main.c b/lib/rust/main.c index cb786c7306ad..40b197fba273 100644 --- a/lib/rust/main.c +++ b/lib/rust/main.c @@ -29,9 +29,4 @@ int main(void) return 0; } -void sample_sleep() -{ - k_msleep(1000); -} - #endif diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs index bdb60799f2de..dd39ecbb19a7 100644 --- a/lib/rust/zephyr-sys/build.rs +++ b/lib/rust/zephyr-sys/build.rs @@ -6,13 +6,30 @@ fn main() { .use_core() // TODO: Fix include paths .clang_arg("-nostdinc") + .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/12.2.0/include-fixed") .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/12.2.0/include") .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/picolibc/include") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/kernel/include") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/arch/arm/include") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated/zephyr") .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/include") .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/soc/st/stm32") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/soc/st/stm32/common") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/drivers") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/soc/st/stm32/stm32f4x") + .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/cmsis/CMSIS/Core/Include") + .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/modules/cmsis") + .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/stm32/stm32cube/stm32f4xx/soc") + .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/stm32/stm32cube/stm32f4xx/drivers/include") + .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/stm32/stm32cube/common_ll/include") + .clang_arg("-isystem/home/ubuntu/zephyrproject/zephyr/lib/libc/common/include") .clang_arg("-imacros/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated/zephyr/autoconf.h") + .clang_arg("-imacros/home/ubuntu/zephyrproject/zephyr/include/zephyr/toolchain/zephyr_stdint.h") + .clang_arg("-DSTM32F411xE") .wrap_static_fns(true) .allowlist_function("gpio_.*") + .allowlist_function("k_.*") .header("wrapper.h") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() diff --git a/lib/rust/zephyr-sys/src/lib.rs b/lib/rust/zephyr-sys/src/lib.rs index d63d629ce07a..9c84cdfcc21b 100644 --- a/lib/rust/zephyr-sys/src/lib.rs +++ b/lib/rust/zephyr-sys/src/lib.rs @@ -3,6 +3,7 @@ #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] +#![allow(improper_ctypes)] use core::include; use core::concat; diff --git a/lib/rust/zephyr-sys/wrapper.h b/lib/rust/zephyr-sys/wrapper.h index e60380b8278d..0bb544bfc199 100644 --- a/lib/rust/zephyr-sys/wrapper.h +++ b/lib/rust/zephyr-sys/wrapper.h @@ -1 +1,2 @@ +#include #include diff --git a/lib/rust/zephyr/src/gpio.rs b/lib/rust/zephyr/src/gpio.rs index 1a80b8cd1250..aa99324b8121 100644 --- a/lib/rust/zephyr/src/gpio.rs +++ b/lib/rust/zephyr/src/gpio.rs @@ -1,13 +1,9 @@ -use zephyr_sys::{gpio_dt_spec, gpio_pin_toggle_dt}; +use zephyr_sys::{gpio_dt_spec, gpio_pin_toggle_dt, k_msleep}; extern "C" { static led: gpio_dt_spec; } -extern "C" { - fn sample_sleep(); -} - pub fn toggle() { unsafe { gpio_pin_toggle_dt(&led); @@ -16,7 +12,7 @@ pub fn toggle() { pub fn sleep() { unsafe { - sample_sleep(); + k_msleep(1000); } } From 57e361ea0e2b20b03903ddd54a452d12521a5f2f Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Thu, 18 Jul 2024 14:10:24 +0200 Subject: [PATCH 26/35] cleaner implementation for allocator and panic handler --- cmake/modules/rust.cmake | 4 +- lib/rust/panic.c | 6 +++ lib/rust/zephyr-sys/build.rs | 1 + lib/rust/zephyr/src/allocator.rs | 19 +++++++++ lib/rust/zephyr/src/lib.rs | 68 ++------------------------------ lib/rust/zephyr/src/panic.rs | 18 +++++++++ samples/rust/hello_rust/prj.conf | 6 +++ 7 files changed, 55 insertions(+), 67 deletions(-) create mode 100644 lib/rust/panic.c create mode 100644 lib/rust/zephyr/src/allocator.rs create mode 100644 lib/rust/zephyr/src/panic.rs diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index b08e972b1b72..c53ead318189 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -106,7 +106,7 @@ ${config_paths} # The library is built by invoking Cargo. add_custom_command( - OUTPUT ${DUMMY_FILE} + OUTPUT ${DUMMY_FILE} /tmp/bindgen/extern.c BYPRODUCTS ${RUST_LIBRARY} COMMAND ${CMAKE_EXECUTABLE} @@ -141,7 +141,7 @@ ${config_paths} # 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) + target_sources(app PRIVATE ${ZEPHYR_BASE}/lib/rust/main.c ${ZEPHYR_BASE}/lib/rust/panic.c) # TODO: Make safer and nicer target_sources(app PRIVATE /tmp/bindgen/extern.c) diff --git a/lib/rust/panic.c b/lib/rust/panic.c new file mode 100644 index 000000000000..642b983ce5fb --- /dev/null +++ b/lib/rust/panic.c @@ -0,0 +1,6 @@ +#include + +void k_panic__extern() +{ + k_panic(); +} diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs index dd39ecbb19a7..185f8efa45de 100644 --- a/lib/rust/zephyr-sys/build.rs +++ b/lib/rust/zephyr-sys/build.rs @@ -30,6 +30,7 @@ fn main() { .wrap_static_fns(true) .allowlist_function("gpio_.*") .allowlist_function("k_.*") + .allowlist_function("printk") .header("wrapper.h") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() diff --git a/lib/rust/zephyr/src/allocator.rs b/lib/rust/zephyr/src/allocator.rs new file mode 100644 index 000000000000..ed1623029f2d --- /dev/null +++ b/lib/rust/zephyr/src/allocator.rs @@ -0,0 +1,19 @@ +use core::alloc::{GlobalAlloc, Layout}; +use zephyr_sys::{k_aligned_alloc, k_free}; + +struct ZephyrAllocator {} + +#[global_allocator] +static ALLOCATOR: ZephyrAllocator = ZephyrAllocator {}; + +unsafe impl Sync for ZephyrAllocator {} + +unsafe impl GlobalAlloc for ZephyrAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + return k_aligned_alloc(layout.align(), layout.size()) as *mut u8; + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { + return k_free(_ptr as *mut core::ffi::c_void); + } +} diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 0c08000bbd2f..528c1668775b 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -10,6 +10,9 @@ extern crate alloc; +mod allocator; +mod panic; + pub mod gpio; // Bring in the generated kconfig module @@ -18,68 +21,3 @@ include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); // Ensure that Rust is enabled. #[cfg(not(CONFIG_RUST))] compile_error!("CONFIG_RUST must be set to build Rust in Zephyr"); - -use core::alloc::{GlobalAlloc, Layout}; -use core::cell::UnsafeCell; -use core::panic::PanicInfo; -use core::ptr::null_mut; -use core::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering::Relaxed; - -/// Override rust's panic. This simplistic initial version just hangs in a loop. -#[panic_handler] -fn panic(_ :&PanicInfo) -> ! { - loop { - } -} - -const ARENA_SIZE: usize = 32 * 1024; -const MAX_SUPPORTED_ALIGN: usize = 4096; - -#[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN -struct SimpleAllocator { - arena: UnsafeCell<[u8; ARENA_SIZE]>, - remaining: AtomicUsize, // we allocate from the top, counting down -} - -#[global_allocator] -static ALLOCATOR: SimpleAllocator = SimpleAllocator { - arena: UnsafeCell::new([0x55; ARENA_SIZE]), - remaining: AtomicUsize::new(ARENA_SIZE), -}; - -unsafe impl Sync for SimpleAllocator {} - -unsafe impl GlobalAlloc for SimpleAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let size = layout.size(); - let align = layout.align(); - - // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2. - // So we can safely use a mask to ensure alignment without worrying about UB. - let align_mask_to_round_down = !(align - 1); - - if align > MAX_SUPPORTED_ALIGN { - return null_mut(); - } - - let mut allocated = 0; - if self - .remaining - .fetch_update(Relaxed, Relaxed, |mut remaining| { - if size > remaining { - return None; - } - remaining -= size; - remaining &= align_mask_to_round_down; - allocated = remaining; - Some(remaining) - }) - .is_err() - { - return null_mut(); - }; - self.arena.get().cast::().add(allocated) - } - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} -} \ No newline at end of file diff --git a/lib/rust/zephyr/src/panic.rs b/lib/rust/zephyr/src/panic.rs new file mode 100644 index 000000000000..05a0d0a01aff --- /dev/null +++ b/lib/rust/zephyr/src/panic.rs @@ -0,0 +1,18 @@ +use alloc::ffi::CString; +use alloc::string::ToString; +use core::ffi::c_char; +use core::panic::PanicInfo; +use zephyr_sys::printk; + +extern "C" { + fn k_panic__extern() -> !; +} + +#[panic_handler] +unsafe fn panic(_info: &PanicInfo) -> ! { + if let Ok(message) = CString::new(_info.to_string()) { + printk("%s\n\0".as_ptr() as *const c_char, message.as_ptr()); + } + + k_panic__extern(); +} diff --git a/samples/rust/hello_rust/prj.conf b/samples/rust/hello_rust/prj.conf index 1251e5ba23e9..c288b9fd4798 100644 --- a/samples/rust/hello_rust/prj.conf +++ b/samples/rust/hello_rust/prj.conf @@ -2,3 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 CONFIG_RUST=y + +CONFIG_MAIN_STACK_SIZE=32768 +CONFIG_HEAP_MEM_POOL_SIZE=32768 + +CONFIG_LOG=y +CONFIG_LOG_PRINTK=y From 1a12f048a964a76871b11f43771dd8bbae6b9d97 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Thu, 18 Jul 2024 15:31:40 +0200 Subject: [PATCH 27/35] better print support --- lib/rust/zephyr/src/lib.rs | 2 +- lib/rust/zephyr/src/print.rs | 22 ++++++++++++++++++++++ samples/rust/hello_rust/src/lib.rs | 17 +++-------------- 3 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 lib/rust/zephyr/src/print.rs diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 528c1668775b..1683b0193ae1 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -12,7 +12,7 @@ extern crate alloc; mod allocator; mod panic; - +pub mod print; pub mod gpio; // Bring in the generated kconfig module diff --git a/lib/rust/zephyr/src/print.rs b/lib/rust/zephyr/src/print.rs new file mode 100644 index 000000000000..62d9d7e11129 --- /dev/null +++ b/lib/rust/zephyr/src/print.rs @@ -0,0 +1,22 @@ +use core::ffi::c_char; +use alloc::ffi::CString; +use zephyr_sys::printk; + +#[macro_export] +macro_rules! println { + ($msg:expr) => { + ::zephyr::print::println_helper($msg); + }; + ($fmt:expr, $($arg:tt)*) => { + let formatted = ::alloc::format!($fmt, $($arg)*); + ::zephyr::print::println_helper(formatted.as_str()); + }; +} + +pub fn println_helper(msg: &str) { + let cstring = CString::new(msg).unwrap(); + + unsafe { + printk("%s\n\0".as_ptr() as *const c_char, cstring.as_ptr() as *const c_char); + } +} diff --git a/samples/rust/hello_rust/src/lib.rs b/samples/rust/hello_rust/src/lib.rs index 355d0bfcd465..aca65c656d8c 100644 --- a/samples/rust/hello_rust/src/lib.rs +++ b/samples/rust/hello_rust/src/lib.rs @@ -6,28 +6,17 @@ // Reference the Zephyr crate so that the panic handler gets used. This is only needed if no // symbols from the crate are directly used. extern crate zephyr; +extern crate alloc; use zephyr::gpio::{sleep, toggle}; +use zephyr::println; #[no_mangle] extern "C" fn rust_main() { - // Until we have allocation, converting a Rust string into a C string is a bit awkward. - // This works because the kconfig values are const. - const BOARD_SRC: &'static str = zephyr::kconfig::CONFIG_BOARD; - let mut board = [0u8; BOARD_SRC.len() + 1]; - board[..BOARD_SRC.len()].clone_from_slice(BOARD_SRC.as_bytes()); - board[BOARD_SRC.len()] = 0; - - unsafe { - printk("Hello world from Rust on %s\n\0".as_ptr(), board.as_ptr()); - } + println!("Hello, world! {}", zephyr::kconfig::CONFIG_BOARD); loop { toggle(); sleep(); } } - -extern "C" { - fn printk(msg: *const u8, arg1: *const u8); -} From 90596999437085f43066b4183dd02f6343114328 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Fri, 19 Jul 2024 10:16:19 +0200 Subject: [PATCH 28/35] more generic way of determining clang args --- cmake/modules/rust.cmake | 69 ++++++++++++++++--- lib/rust/zephyr-sys/Cargo.toml | 2 - .../zephyr-sys/{wrapper.h => bindgen_input.h} | 0 lib/rust/zephyr-sys/build.rs | 40 ++++------- 4 files changed, 72 insertions(+), 39 deletions(-) rename lib/rust/zephyr-sys/{wrapper.h => bindgen_input.h} (100%) diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index c53ead318189..445e9e49b92a 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -39,6 +39,50 @@ function(_rust_map_target) endif() endfunction() +function(_generate_clang_args BINDGEN_CLANG_ARGS) + # Get compiler arguments from Zephyr + zephyr_get_system_include_directories_for_lang(C system_includes) + zephyr_get_include_directories_for_lang(C includes) + zephyr_get_compile_definitions_for_lang(C definitions) + + # Gather -imacros options + set(options "-imacros${AUTOCONF_H}") + + if(CONFIG_ENFORCE_ZEPHYR_STDINT) + list(APPEND options "-imacros${ZEPHYR_BASE}/include/zephyr/toolchain/zephyr_stdint.h") + endif() + + # Determine standard include directories of compiler + file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/empty.c) + + execute_process( + COMMAND ${CMAKE_C_COMPILER} -E -Wp,-v ${CMAKE_CURRENT_BINARY_DIR}/empty.c + OUTPUT_QUIET + ERROR_VARIABLE output + COMMAND_ERROR_IS_FATAL ANY + ) + + set(standard_includes "-nostdinc") + if(output MATCHES "#include <\.\.\.> search starts here:\n(.*)\nEnd of search list\.") + string(REGEX MATCHALL "[^ \n]+" paths "${CMAKE_MATCH_1}") + foreach(path ${paths}) + get_filename_component(path ${path} ABSOLUTE) + list(APPEND standard_includes "-isystem${path}") + endforeach() + else() + message(WARNING "Unable to determine compiler standard include directories.") + endif() + + # Generate file containing arguments for clang. Note that the file is generated after the + # CMake configure stage as the variables contain generator expressions which cannot be + # evaluated right now. + file( + GENERATE + OUTPUT ${BINDGEN_CLANG_ARGS} + CONTENT "${standard_includes};${system_includes};${includes};${definitions};${options}" + ) +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 @@ -58,6 +102,11 @@ function(rust_cargo_application) 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") + set(BINDGEN_CLANG_ARGS "${CMAKE_CURRENT_BINARY_DIR}/rust/clang_args.txt") + set(BINDGEN_WRAP_STATIC_FNS "${CARGO_TARGET_DIR}/${RUST_TARGET}/${RUST_BUILD_TYPE}/wrap_static_fns.c") + + _generate_clang_args(${BINDGEN_CLANG_ARGS}) + # 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 @@ -99,6 +148,8 @@ target-dir = \"${CARGO_TARGET_DIR}\" BUILD_DIR = \"${CMAKE_CURRENT_BINARY_DIR}\" DOTCONFIG = \"${DOTCONFIG}\" ZEPHYR_DTS = \"${ZEPHYR_DTS}\" +BINDGEN_CLANG_ARGS = \"${BINDGEN_CLANG_ARGS}\" +BINDGEN_WRAP_STATIC_FNS = \"${BINDGEN_WRAP_STATIC_FNS}\" [patch.crates-io] ${config_paths} @@ -106,13 +157,15 @@ ${config_paths} # The library is built by invoking Cargo. add_custom_command( - OUTPUT ${DUMMY_FILE} /tmp/bindgen/extern.c - BYPRODUCTS ${RUST_LIBRARY} + OUTPUT ${DUMMY_FILE} + BYPRODUCTS ${RUST_LIBRARY} ${BINDGEN_WRAP_STATIC_FNS} COMMAND ${CMAKE_EXECUTABLE} env BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR} DOTCONFIG=${DOTCONFIG} ZEPHYR_DTS=${ZEPHYR_DTS} + BINDGEN_CLANG_ARGS=${BINDGEN_CLANG_ARGS} + BINDGEN_WRAP_STATIC_FNS="${BINDGEN_WRAP_STATIC_FNS}" cargo build # TODO: release flag if release build # --release @@ -139,11 +192,9 @@ ${config_paths} 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 ${ZEPHYR_BASE}/lib/rust/panic.c) - - # TODO: Make safer and nicer - target_sources(app PRIVATE /tmp/bindgen/extern.c) - target_include_directories(app PRIVATE ${ZEPHYR_BASE}/lib/rust/zephyr-sys) + target_sources(app PRIVATE + ${ZEPHYR_BASE}/lib/rust/main.c + ${ZEPHYR_BASE}/lib/rust/panic.c + ${BINDGEN_WRAP_STATIC_FNS} + ) endfunction() diff --git a/lib/rust/zephyr-sys/Cargo.toml b/lib/rust/zephyr-sys/Cargo.toml index 9dae6209ccb3..f33c729c7cba 100644 --- a/lib/rust/zephyr-sys/Cargo.toml +++ b/lib/rust/zephyr-sys/Cargo.toml @@ -3,7 +3,5 @@ name = "zephyr-sys" version = "0.1.0" edition = "2021" -[dependencies] - [build-dependencies] bindgen = { version = "0.69.4", features = ["experimental"] } diff --git a/lib/rust/zephyr-sys/wrapper.h b/lib/rust/zephyr-sys/bindgen_input.h similarity index 100% rename from lib/rust/zephyr-sys/wrapper.h rename to lib/rust/zephyr-sys/bindgen_input.h diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs index 185f8efa45de..0546a1a7fae6 100644 --- a/lib/rust/zephyr-sys/build.rs +++ b/lib/rust/zephyr-sys/build.rs @@ -1,42 +1,26 @@ -use std::env; +use std::{env, fs}; use std::path::PathBuf; fn main() { + let input_header = env::current_dir().unwrap().join("bindgen_input.h"); + let out_path = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR must be set!")); + let wrap_static_fns = PathBuf::from(env::var("BINDGEN_WRAP_STATIC_FNS").expect("BINDGEN_WRAP_STATIC_FNS must be set!")); + let clang_args_path = PathBuf::from(env::var("BINDGEN_CLANG_ARGS").expect("BINDGEN_CLANG_ARGS must be set!")); + let clang_args = fs::read_to_string(clang_args_path).expect("Failed to read BINDGEN_CLANG_ARGS file!"); + let bindings = bindgen::Builder::default() .use_core() - // TODO: Fix include paths - .clang_arg("-nostdinc") - .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/12.2.0/include-fixed") - .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/lib/gcc/arm-zephyr-eabi/12.2.0/include") - .clang_arg("-isystem/opt/zephyr-sdk-0.16.8/arm-zephyr-eabi/picolibc/include") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/kernel/include") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/arch/arm/include") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated/zephyr") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/include") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/soc/st/stm32") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/soc/st/stm32/common") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/drivers") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/soc/st/stm32/stm32f4x") - .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/cmsis/CMSIS/Core/Include") - .clang_arg("-I/home/ubuntu/zephyrproject/zephyr/modules/cmsis") - .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/stm32/stm32cube/stm32f4xx/soc") - .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/stm32/stm32cube/stm32f4xx/drivers/include") - .clang_arg("-I/home/ubuntu/zephyrproject/modules/hal/stm32/stm32cube/common_ll/include") - .clang_arg("-isystem/home/ubuntu/zephyrproject/zephyr/lib/libc/common/include") - .clang_arg("-imacros/home/ubuntu/zephyrproject/zephyr/build/zephyr/include/generated/zephyr/autoconf.h") - .clang_arg("-imacros/home/ubuntu/zephyrproject/zephyr/include/zephyr/toolchain/zephyr_stdint.h") - .clang_arg("-DSTM32F411xE") + .detect_include_paths(false) .wrap_static_fns(true) + .wrap_static_fns_path(wrap_static_fns) .allowlist_function("gpio_.*") .allowlist_function("k_.*") .allowlist_function("printk") - .header("wrapper.h") + .clang_args(clang_args.split(';')) + .header(input_header.to_str().unwrap()) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() - .expect("Unable to generate bindings"); - - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + .expect("Unable to generate bindings!"); bindings .write_to_file(out_path.join("bindings.rs")) From f041979f86ff88732bf08cc0bf6d704dc91f8c0c Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Fri, 19 Jul 2024 11:23:57 +0200 Subject: [PATCH 29/35] move gpio driver --- lib/rust/zephyr/src/drivers.rs | 1 + lib/rust/zephyr/src/{ => drivers}/gpio.rs | 0 lib/rust/zephyr/src/lib.rs | 2 +- samples/rust/hello_rust/src/lib.rs | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 lib/rust/zephyr/src/drivers.rs rename lib/rust/zephyr/src/{ => drivers}/gpio.rs (100%) diff --git a/lib/rust/zephyr/src/drivers.rs b/lib/rust/zephyr/src/drivers.rs new file mode 100644 index 000000000000..39e634708b19 --- /dev/null +++ b/lib/rust/zephyr/src/drivers.rs @@ -0,0 +1 @@ +pub mod gpio; \ No newline at end of file diff --git a/lib/rust/zephyr/src/gpio.rs b/lib/rust/zephyr/src/drivers/gpio.rs similarity index 100% rename from lib/rust/zephyr/src/gpio.rs rename to lib/rust/zephyr/src/drivers/gpio.rs diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 1683b0193ae1..7c73149679ce 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -13,7 +13,7 @@ extern crate alloc; mod allocator; mod panic; pub mod print; -pub mod gpio; +pub mod drivers; // Bring in the generated kconfig module include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); diff --git a/samples/rust/hello_rust/src/lib.rs b/samples/rust/hello_rust/src/lib.rs index aca65c656d8c..330081ddb999 100644 --- a/samples/rust/hello_rust/src/lib.rs +++ b/samples/rust/hello_rust/src/lib.rs @@ -8,7 +8,7 @@ extern crate zephyr; extern crate alloc; -use zephyr::gpio::{sleep, toggle}; +use zephyr::drivers::gpio::{sleep, toggle}; use zephyr::println; #[no_mangle] From fe62b92b5f1948c3a600c7ef1fb34d2a33897f9c Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Fri, 19 Jul 2024 13:43:09 +0200 Subject: [PATCH 30/35] start implementing gpio driver --- cmake/modules/rust.cmake | 2 ++ lib/rust/main.c | 9 ----- lib/rust/zephyr-sys/build.rs | 11 ++++-- lib/rust/zephyr/Cargo.toml | 1 + lib/rust/zephyr/src/drivers/gpio.rs | 53 +++++++++++++++++++++++------ lib/rust/zephyr/src/kernel.rs | 3 ++ lib/rust/zephyr/src/lib.rs | 2 ++ samples/rust/hello_rust/prj.conf | 3 -- samples/rust/hello_rust/src/lib.rs | 11 ++++-- scripts/dts/gen_dts_rust.py | 14 +++++--- 10 files changed, 76 insertions(+), 33 deletions(-) create mode 100644 lib/rust/zephyr/src/kernel.rs diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index 445e9e49b92a..142982b91f8c 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -148,6 +148,7 @@ target-dir = \"${CARGO_TARGET_DIR}\" BUILD_DIR = \"${CMAKE_CURRENT_BINARY_DIR}\" DOTCONFIG = \"${DOTCONFIG}\" ZEPHYR_DTS = \"${ZEPHYR_DTS}\" +ZEPHYR_BASE = \"${ZEPHYR_BASE}\" BINDGEN_CLANG_ARGS = \"${BINDGEN_CLANG_ARGS}\" BINDGEN_WRAP_STATIC_FNS = \"${BINDGEN_WRAP_STATIC_FNS}\" @@ -164,6 +165,7 @@ ${config_paths} env BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR} DOTCONFIG=${DOTCONFIG} ZEPHYR_DTS=${ZEPHYR_DTS} + ZEPHYR_BASE=${ZEPHYR_BASE} BINDGEN_CLANG_ARGS=${BINDGEN_CLANG_ARGS} BINDGEN_WRAP_STATIC_FNS="${BINDGEN_WRAP_STATIC_FNS}" cargo build diff --git a/lib/rust/main.c b/lib/rust/main.c index 40b197fba273..e5ba92d914d1 100644 --- a/lib/rust/main.c +++ b/lib/rust/main.c @@ -16,15 +16,6 @@ const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); int main(void) { - if (!gpio_is_ready_dt(&led)) { - return 0; - } - - int ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); - if (ret < 0) { - return 0; - } - rust_main(); return 0; } diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs index 0546a1a7fae6..5d927b76a922 100644 --- a/lib/rust/zephyr-sys/build.rs +++ b/lib/rust/zephyr-sys/build.rs @@ -4,18 +4,23 @@ use std::path::PathBuf; fn main() { let input_header = env::current_dir().unwrap().join("bindgen_input.h"); let out_path = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR must be set!")); + let zephyr_base = PathBuf::from(env::var("ZEPHYR_BASE").expect("ZEPHYR_BASE must be set!")); let wrap_static_fns = PathBuf::from(env::var("BINDGEN_WRAP_STATIC_FNS").expect("BINDGEN_WRAP_STATIC_FNS must be set!")); let clang_args_path = PathBuf::from(env::var("BINDGEN_CLANG_ARGS").expect("BINDGEN_CLANG_ARGS must be set!")); let clang_args = fs::read_to_string(clang_args_path).expect("Failed to read BINDGEN_CLANG_ARGS file!"); let bindings = bindgen::Builder::default() .use_core() + .layout_tests(false) .detect_include_paths(false) .wrap_static_fns(true) .wrap_static_fns_path(wrap_static_fns) - .allowlist_function("gpio_.*") - .allowlist_function("k_.*") - .allowlist_function("printk") + .allowlist_file(zephyr_base.join(".*").to_str().unwrap()) + .blocklist_function("z_impl_.*") + .blocklist_var("K_SYSCALL_.*") + .blocklist_var("DT_.*") + .blocklist_var("CONFIG_.*") + .blocklist_var("Z_UTIL_.*") .clang_args(clang_args.split(';')) .header(input_header.to_str().unwrap()) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) diff --git a/lib/rust/zephyr/Cargo.toml b/lib/rust/zephyr/Cargo.toml index 02f73d1dd1fb..e5adac1833b6 100644 --- a/lib/rust/zephyr/Cargo.toml +++ b/lib/rust/zephyr/Cargo.toml @@ -11,6 +11,7 @@ Functionality for Rust-based applications that run on Zephyr. [dependencies] zephyr-sys = "0.1.0" +bitmask-enum = "2.2.4" # These are needed at build time. # Whether these need to be vendored is an open question. They are not diff --git a/lib/rust/zephyr/src/drivers/gpio.rs b/lib/rust/zephyr/src/drivers/gpio.rs index aa99324b8121..f3c8ff4115fe 100644 --- a/lib/rust/zephyr/src/drivers/gpio.rs +++ b/lib/rust/zephyr/src/drivers/gpio.rs @@ -1,18 +1,51 @@ -use zephyr_sys::{gpio_dt_spec, gpio_pin_toggle_dt, k_msleep}; +use bitmask_enum::bitmask; extern "C" { - static led: gpio_dt_spec; + static led: zephyr_sys::gpio_dt_spec; } -pub fn toggle() { - unsafe { - gpio_pin_toggle_dt(&led); - } +#[bitmask(u32)] +pub enum Flags { + Input = zephyr_sys::GPIO_INPUT, + Output = zephyr_sys::GPIO_OUTPUT, + Disconnected = zephyr_sys::GPIO_DISCONNECTED, + InitLow = zephyr_sys::GPIO_OUTPUT_INIT_LOW, + InitHigh = zephyr_sys::GPIO_OUTPUT_INIT_HIGH, + InitLogical = zephyr_sys::GPIO_OUTPUT_INIT_LOGICAL, + OutputLow = zephyr_sys::GPIO_OUTPUT | zephyr_sys::GPIO_OUTPUT_INIT_LOW, + OutputHigh = zephyr_sys::GPIO_OUTPUT | zephyr_sys::GPIO_OUTPUT_INIT_HIGH, + OutputInactive = zephyr_sys::GPIO_OUTPUT | zephyr_sys::GPIO_OUTPUT_INIT_LOW | zephyr_sys::GPIO_OUTPUT_INIT_LOGICAL, + OutputActive = zephyr_sys::GPIO_OUTPUT | zephyr_sys::GPIO_OUTPUT_INIT_HIGH | zephyr_sys::GPIO_OUTPUT_INIT_LOGICAL, } -pub fn sleep() { - unsafe { - k_msleep(1000); - } +pub struct Pin { + gpio_dt_spec: zephyr_sys::gpio_dt_spec, } +impl Pin { + pub fn get_led() -> Pin { + unsafe { + return Pin { + gpio_dt_spec: led.clone(), + } + } + } + + pub fn configure(&self, extra_flags: Flags) -> Result<(), i32>{ + unsafe { + match zephyr_sys::gpio_pin_configure_dt(&self.gpio_dt_spec, extra_flags.bits()) { + 0 => Ok(()), + res => Err(res), + } + } + } + + pub fn toggle(&self) -> Result<(), i32> { + unsafe { + match zephyr_sys::gpio_pin_toggle_dt(&self.gpio_dt_spec) { + 0 => Ok(()), + res => Err(res), + } + } + } +} diff --git a/lib/rust/zephyr/src/kernel.rs b/lib/rust/zephyr/src/kernel.rs new file mode 100644 index 000000000000..f9f7803e29a9 --- /dev/null +++ b/lib/rust/zephyr/src/kernel.rs @@ -0,0 +1,3 @@ +pub fn msleep(ms: i32) -> i32 { + unsafe { zephyr_sys::k_msleep(ms) } +} \ No newline at end of file diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 7c73149679ce..34751f973e18 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -12,6 +12,8 @@ extern crate alloc; mod allocator; mod panic; + +pub mod kernel; pub mod print; pub mod drivers; diff --git a/samples/rust/hello_rust/prj.conf b/samples/rust/hello_rust/prj.conf index c288b9fd4798..f39edba40263 100644 --- a/samples/rust/hello_rust/prj.conf +++ b/samples/rust/hello_rust/prj.conf @@ -5,6 +5,3 @@ CONFIG_RUST=y CONFIG_MAIN_STACK_SIZE=32768 CONFIG_HEAP_MEM_POOL_SIZE=32768 - -CONFIG_LOG=y -CONFIG_LOG_PRINTK=y diff --git a/samples/rust/hello_rust/src/lib.rs b/samples/rust/hello_rust/src/lib.rs index 330081ddb999..ea94b7917902 100644 --- a/samples/rust/hello_rust/src/lib.rs +++ b/samples/rust/hello_rust/src/lib.rs @@ -8,15 +8,20 @@ extern crate zephyr; extern crate alloc; -use zephyr::drivers::gpio::{sleep, toggle}; +use zephyr::kernel::msleep; +use zephyr::drivers::gpio; use zephyr::println; #[no_mangle] extern "C" fn rust_main() { println!("Hello, world! {}", zephyr::kconfig::CONFIG_BOARD); + let pin = gpio::Pin::get_led(); + + pin.configure(gpio::Flags::OutputActive).expect("Failed to configure pin."); + loop { - toggle(); - sleep(); + msleep(1000); + pin.toggle().expect("Failed to toggle pin."); } } diff --git a/scripts/dts/gen_dts_rust.py b/scripts/dts/gen_dts_rust.py index f64fd4d5aa7f..95e35766b9ba 100755 --- a/scripts/dts/gen_dts_rust.py +++ b/scripts/dts/gen_dts_rust.py @@ -73,6 +73,8 @@ def generate_node_struct(node): def generate_node_instance(node): for label in node.labels: yield f'// label("{label}")\n' + for alias in node.aliases: + yield f'// alias("{alias}")\n' if node.matching_compat: yield f'// matching_compat("{node.matching_compat}")\n' @@ -84,20 +86,22 @@ def generate_node_instance(node): for property_name, property_object in node.props.items(): if property_object.spec.type == 'boolean': yield f' {replace_chars(property_name)}: {"true" if property_object.val else "false"},\n' - if property_object.spec.type == 'int': + elif property_object.spec.type == 'int': yield f' {replace_chars(property_name)}: {property_object.val},\n' - if property_object.spec.type == 'string': + elif property_object.spec.type == 'string': yield f' {replace_chars(property_name)}: "{property_object.val}",\n' - if property_object.spec.type == 'array': + elif property_object.spec.type == 'array': yield f' {replace_chars(property_name)}: [' yield ', '.join(str(val) for val in property_object.val) yield '],\n' - if property_object.spec.type == 'string-array': + elif property_object.spec.type == 'string-array': yield f' {replace_chars(property_name)}: [' yield ', '.join(f'"{val}"' for val in property_object.val) yield '],\n' - if property_object.spec.type == 'phandle' or property_object.spec.type == 'path': + elif property_object.spec.type == 'phandle' or property_object.spec.type == 'path': yield f' {replace_chars(property_name)}: "DT_NODE_{property_object.val.dep_ordinal}",\n' + else: + yield f' // {replace_chars(property_name)} ({property_object.spec.type})\n' yield '};\n\n' From f80d2191defba514655004d906c95bf10eb0dedb Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Fri, 19 Jul 2024 14:26:37 +0200 Subject: [PATCH 31/35] improved errno handling --- lib/rust/zephyr-sys/bindgen_input.h | 1 + lib/rust/zephyr-sys/build.rs | 1 + lib/rust/zephyr/Cargo.toml | 1 + lib/rust/zephyr/src/drivers/gpio.rs | 15 ++--- lib/rust/zephyr/src/errno.rs | 100 ++++++++++++++++++++++++++++ lib/rust/zephyr/src/lib.rs | 1 + 6 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 lib/rust/zephyr/src/errno.rs diff --git a/lib/rust/zephyr-sys/bindgen_input.h b/lib/rust/zephyr-sys/bindgen_input.h index 0bb544bfc199..3e6a76154b99 100644 --- a/lib/rust/zephyr-sys/bindgen_input.h +++ b/lib/rust/zephyr-sys/bindgen_input.h @@ -1,2 +1,3 @@ +#include #include #include diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs index 5d927b76a922..3952364cd2bd 100644 --- a/lib/rust/zephyr-sys/build.rs +++ b/lib/rust/zephyr-sys/build.rs @@ -16,6 +16,7 @@ fn main() { .wrap_static_fns(true) .wrap_static_fns_path(wrap_static_fns) .allowlist_file(zephyr_base.join(".*").to_str().unwrap()) + .allowlist_file(".*/errno.h") .blocklist_function("z_impl_.*") .blocklist_var("K_SYSCALL_.*") .blocklist_var("DT_.*") diff --git a/lib/rust/zephyr/Cargo.toml b/lib/rust/zephyr/Cargo.toml index e5adac1833b6..28259d5a5afd 100644 --- a/lib/rust/zephyr/Cargo.toml +++ b/lib/rust/zephyr/Cargo.toml @@ -12,6 +12,7 @@ Functionality for Rust-based applications that run on Zephyr. [dependencies] zephyr-sys = "0.1.0" bitmask-enum = "2.2.4" +int-enum = "1.1.2" # These are needed at build time. # Whether these need to be vendored is an open question. They are not diff --git a/lib/rust/zephyr/src/drivers/gpio.rs b/lib/rust/zephyr/src/drivers/gpio.rs index f3c8ff4115fe..3109a384a861 100644 --- a/lib/rust/zephyr/src/drivers/gpio.rs +++ b/lib/rust/zephyr/src/drivers/gpio.rs @@ -1,4 +1,5 @@ use bitmask_enum::bitmask; +use crate::errno::{Errno, parse_result}; extern "C" { static led: zephyr_sys::gpio_dt_spec; @@ -31,21 +32,15 @@ impl Pin { } } - pub fn configure(&self, extra_flags: Flags) -> Result<(), i32>{ + pub fn configure(&self, extra_flags: Flags) -> Result<(), Errno>{ unsafe { - match zephyr_sys::gpio_pin_configure_dt(&self.gpio_dt_spec, extra_flags.bits()) { - 0 => Ok(()), - res => Err(res), - } + parse_result(zephyr_sys::gpio_pin_configure_dt(&self.gpio_dt_spec, extra_flags.bits())) } } - pub fn toggle(&self) -> Result<(), i32> { + pub fn toggle(&self) -> Result<(), Errno> { unsafe { - match zephyr_sys::gpio_pin_toggle_dt(&self.gpio_dt_spec) { - 0 => Ok(()), - res => Err(res), - } + parse_result(zephyr_sys::gpio_pin_toggle_dt(&self.gpio_dt_spec)) } } } diff --git a/lib/rust/zephyr/src/errno.rs b/lib/rust/zephyr/src/errno.rs new file mode 100644 index 000000000000..37a1106981d8 --- /dev/null +++ b/lib/rust/zephyr/src/errno.rs @@ -0,0 +1,100 @@ +use int_enum::IntEnum; + +#[repr(u32)] +#[derive(Debug, PartialEq, IntEnum)] +pub enum Errno { + EPERM = zephyr_sys::EPERM, + ENOENT = zephyr_sys::ENOENT, + ESRCH = zephyr_sys::ESRCH, + EINTR = zephyr_sys::EINTR, + EIO = zephyr_sys::EIO, + ENXIO = zephyr_sys::ENXIO, + E2BIG = zephyr_sys::E2BIG, + ENOEXEC = zephyr_sys::ENOEXEC, + EBADF = zephyr_sys::EBADF, + ECHILD = zephyr_sys::ECHILD, + EAGAIN = zephyr_sys::EAGAIN, + ENOMEM = zephyr_sys::ENOMEM, + EACCES = zephyr_sys::EACCES, + EFAULT = zephyr_sys::EFAULT, + ENOTBLK = zephyr_sys::ENOTBLK, + EBUSY = zephyr_sys::EBUSY, + EEXIST = zephyr_sys::EEXIST, + EXDEV = zephyr_sys::EXDEV, + ENODEV = zephyr_sys::ENODEV, + ENOTDIR = zephyr_sys::ENOTDIR, + EISDIR = zephyr_sys::EISDIR, + EINVAL = zephyr_sys::EINVAL, + ENFILE = zephyr_sys::ENFILE, + EMFILE = zephyr_sys::EMFILE, + ENOTTY = zephyr_sys::ENOTTY, + ETXTBSY = zephyr_sys::ETXTBSY, + EFBIG = zephyr_sys::EFBIG, + ENOSPC = zephyr_sys::ENOSPC, + ESPIPE = zephyr_sys::ESPIPE, + EROFS = zephyr_sys::EROFS, + EMLINK = zephyr_sys::EMLINK, + EPIPE = zephyr_sys::EPIPE, + EDOM = zephyr_sys::EDOM, + ERANGE = zephyr_sys::ERANGE, + ENOMSG = zephyr_sys::ENOMSG, + EDEADLK = zephyr_sys::EDEADLK, + ENOLCK = zephyr_sys::ENOLCK, + ENOSTR = zephyr_sys::ENOSTR, + ENODATA = zephyr_sys::ENODATA, + ETIME = zephyr_sys::ETIME, + ENOSR = zephyr_sys::ENOSR, + EPROTO = zephyr_sys::EPROTO, + EBADMSG = zephyr_sys::EBADMSG, + ENOSYS = zephyr_sys::ENOSYS, + ENOTEMPTY = zephyr_sys::ENOTEMPTY, + ENAMETOOLONG = zephyr_sys::ENAMETOOLONG, + ELOOP = zephyr_sys::ELOOP, + EOPNOTSUPP = zephyr_sys::EOPNOTSUPP, + EPFNOSUPPORT = zephyr_sys::EPFNOSUPPORT, + ECONNRESET = zephyr_sys::ECONNRESET, + ENOBUFS = zephyr_sys::ENOBUFS, + EAFNOSUPPORT = zephyr_sys::EAFNOSUPPORT, + EPROTOTYPE = zephyr_sys::EPROTOTYPE, + ENOTSOCK = zephyr_sys::ENOTSOCK, + ENOPROTOOPT = zephyr_sys::ENOPROTOOPT, + ESHUTDOWN = zephyr_sys::ESHUTDOWN, + ECONNREFUSED = zephyr_sys::ECONNREFUSED, + EADDRINUSE = zephyr_sys::EADDRINUSE, + ECONNABORTED = zephyr_sys::ECONNABORTED, + ENETUNREACH = zephyr_sys::ENETUNREACH, + ENETDOWN = zephyr_sys::ENETDOWN, + ETIMEDOUT = zephyr_sys::ETIMEDOUT, + EHOSTDOWN = zephyr_sys::EHOSTDOWN, + EHOSTUNREACH = zephyr_sys::EHOSTUNREACH, + EINPROGRESS = zephyr_sys::EINPROGRESS, + EALREADY = zephyr_sys::EALREADY, + EDESTADDRREQ = zephyr_sys::EDESTADDRREQ, + EMSGSIZE = zephyr_sys::EMSGSIZE, + EPROTONOSUPPORT = zephyr_sys::EPROTONOSUPPORT, + ESOCKTNOSUPPORT = zephyr_sys::ESOCKTNOSUPPORT, + EADDRNOTAVAIL = zephyr_sys::EADDRNOTAVAIL, + ENETRESET = zephyr_sys::ENETRESET, + EISCONN = zephyr_sys::EISCONN, + ENOTCONN = zephyr_sys::ENOTCONN, + ETOOMANYREFS = zephyr_sys::ETOOMANYREFS, + ENOTSUP = zephyr_sys::ENOTSUP, + EILSEQ = zephyr_sys::EILSEQ, + EOVERFLOW = zephyr_sys::EOVERFLOW, + ECANCELED = zephyr_sys::ECANCELED, +} + +pub fn parse_result(result: i32) -> Result<(), Errno> { + parse_result_with_value(result).map(|_| ()) +} + +pub fn parse_result_with_value(result: i32) -> Result { + if result >= 0 { + return Ok(result) + } + + match Errno::try_from(-result as u32) { + Ok(errno) => Err(errno), + _ => panic!("Unexpected value"), + } +} diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 34751f973e18..952a06876566 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -13,6 +13,7 @@ extern crate alloc; mod allocator; mod panic; +pub mod errno; pub mod kernel; pub mod print; pub mod drivers; From 8f14bb74efc7c59b2c089d3ddc4c0fa7c111ca75 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Fri, 19 Jul 2024 15:23:29 +0200 Subject: [PATCH 32/35] support more types in rust dts --- scripts/dts/gen_dts_rust.py | 50 +++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/scripts/dts/gen_dts_rust.py b/scripts/dts/gen_dts_rust.py index 95e35766b9ba..0fa4b8cf0d76 100755 --- a/scripts/dts/gen_dts_rust.py +++ b/scripts/dts/gen_dts_rust.py @@ -38,6 +38,8 @@ def generate_dts_rust(edt): def generate_node_struct(node): + phandle_arrays = {} + yield f'struct DtNode{node.dep_ordinal} {{\n' for child_name, child_node in node.children.items(): @@ -52,25 +54,42 @@ def generate_node_struct(node): rust_type = 'u32' if property_object.spec.type == 'string': rust_type = 'str' + if property_object.spec.type == 'uint8-array': + rust_type = '[u8]' if property_object.spec.type == 'array': rust_type = '[u32]' if property_object.spec.type == 'string-array': rust_type = '[str]' - if property_object.spec.type == 'phandle' or property_object.spec.type == 'path': + if property_object.spec.type in ('phandle', 'path'): rust_type = f'&DtNode{property_object.val.dep_ordinal}' + if property_object.spec.type == 'phandles': + rust_type = '(' + ', '.join(f'&DtNode{n.dep_ordinal}' for n in property_object.val) + ')' if property_object.spec.type == 'phandle-array': - pass + pha_types = [] + for pha_entry in property_object.val: + pha_type = f'DtNode{node.dep_ordinal}Pha{len(phandle_arrays)}' + pha_types.append(pha_type) + phandle_arrays[pha_type] = pha_entry + rust_type = '(' + ', '.join('&' + pha_id for pha_id in pha_types) + ')' if rust_type: yield f' {replace_chars(property_name)}: {rust_type},\n' else: yield f' // {replace_chars(property_name)} ({property_object.spec.type})\n' - yield '}\n\n' + for pha_type, pha_entry in phandle_arrays.items(): + yield f'struct {pha_type} {{\n' + yield f' controller: &DtNode{pha_entry.controller.dep_ordinal},\n' + for cell_name in pha_entry.data.keys(): + yield f' {replace_chars(cell_name)}: u32,\n' + yield '}\n\n' + def generate_node_instance(node): + phandle_arrays = {} + for label in node.labels: yield f'// label("{label}")\n' for alias in node.aliases: @@ -90,7 +109,7 @@ def generate_node_instance(node): yield f' {replace_chars(property_name)}: {property_object.val},\n' elif property_object.spec.type == 'string': yield f' {replace_chars(property_name)}: "{property_object.val}",\n' - elif property_object.spec.type == 'array': + elif property_object.spec.type in ('uint8-array', 'array'): yield f' {replace_chars(property_name)}: [' yield ', '.join(str(val) for val in property_object.val) yield '],\n' @@ -98,13 +117,34 @@ def generate_node_instance(node): yield f' {replace_chars(property_name)}: [' yield ', '.join(f'"{val}"' for val in property_object.val) yield '],\n' - elif property_object.spec.type == 'phandle' or property_object.spec.type == 'path': + elif property_object.spec.type in ('phandle', 'path'): yield f' {replace_chars(property_name)}: "DT_NODE_{property_object.val.dep_ordinal}",\n' + elif property_object.spec.type == 'phandles': + yield f' {replace_chars(property_name)}: (' + yield ', '.join(f'DT_NODE_{n.dep_ordinal}' for n in property_object.val) + yield '),\n' + elif property_object.spec.type == 'phandle-array': + pha_ids = [] + for pha_entry in property_object.val: + pha_inst = f'DT_NODE_{node.dep_ordinal}_PHA_{len(phandle_arrays)}' + pha_type = f'&DtNode{node.dep_ordinal}Pha{len(phandle_arrays)}' + pha_ids.append(pha_inst) + phandle_arrays[pha_inst] = (pha_type, pha_entry) + yield f' {replace_chars(property_name)}: (' + ', '.join(pha_ids) + '),\n' else: yield f' // {replace_chars(property_name)} ({property_object.spec.type})\n' yield '};\n\n' + for pha_inst, (pha_type, pha_entry) in phandle_arrays.items(): + if pha_entry.name: + yield f'// {pha_entry.name}\n' + yield f'const {pha_inst}: {pha_type} = {pha_type} {{\n' + yield f' controller: DT_NODE_{pha_entry.controller.dep_ordinal},\n' + for cell_name, cell_value in pha_entry.data.items(): + yield f' {replace_chars(cell_name)}: {cell_value},\n' + yield '};\n\n' + def replace_chars(text: str): return re.sub(r'\W+', '_', text) From 9eb9540316f7ac36f017fdef945e3ce9df3f9361 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Fri, 19 Jul 2024 16:06:48 +0200 Subject: [PATCH 33/35] include generated device tree --- cmake/modules/rust.cmake | 6 +- lib/rust/zephyr/src/devicetree.rs | 1 + lib/rust/zephyr/src/lib.rs | 1 + scripts/dts/gen_dts_rust.py | 103 ++++++++++++++++-------------- 4 files changed, 62 insertions(+), 49 deletions(-) create mode 100644 lib/rust/zephyr/src/devicetree.rs diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index 142982b91f8c..58b5ed670cf2 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -102,6 +102,8 @@ function(rust_cargo_application) 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") + set(RUST_DTS "${CMAKE_BINARY_DIR}/zephyr/dts.rs") + set(BINDGEN_CLANG_ARGS "${CMAKE_CURRENT_BINARY_DIR}/rust/clang_args.txt") set(BINDGEN_WRAP_STATIC_FNS "${CARGO_TARGET_DIR}/${RUST_TARGET}/${RUST_BUILD_TYPE}/wrap_static_fns.c") @@ -147,8 +149,8 @@ target-dir = \"${CARGO_TARGET_DIR}\" [env] BUILD_DIR = \"${CMAKE_CURRENT_BINARY_DIR}\" DOTCONFIG = \"${DOTCONFIG}\" -ZEPHYR_DTS = \"${ZEPHYR_DTS}\" ZEPHYR_BASE = \"${ZEPHYR_BASE}\" +RUST_DTS = \"${RUST_DTS}\" BINDGEN_CLANG_ARGS = \"${BINDGEN_CLANG_ARGS}\" BINDGEN_WRAP_STATIC_FNS = \"${BINDGEN_WRAP_STATIC_FNS}\" @@ -164,8 +166,8 @@ ${config_paths} ${CMAKE_EXECUTABLE} env BUILD_DIR=${CMAKE_CURRENT_BINARY_DIR} DOTCONFIG=${DOTCONFIG} - ZEPHYR_DTS=${ZEPHYR_DTS} ZEPHYR_BASE=${ZEPHYR_BASE} + RUST_DTS=${RUST_DTS} BINDGEN_CLANG_ARGS=${BINDGEN_CLANG_ARGS} BINDGEN_WRAP_STATIC_FNS="${BINDGEN_WRAP_STATIC_FNS}" cargo build diff --git a/lib/rust/zephyr/src/devicetree.rs b/lib/rust/zephyr/src/devicetree.rs new file mode 100644 index 000000000000..0546c921689e --- /dev/null +++ b/lib/rust/zephyr/src/devicetree.rs @@ -0,0 +1 @@ +include!(env!("RUST_DTS")); diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 952a06876566..5d1fa32a0281 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; mod allocator; mod panic; +mod devicetree; pub mod errno; pub mod kernel; diff --git a/scripts/dts/gen_dts_rust.py b/scripts/dts/gen_dts_rust.py index 0fa4b8cf0d76..230375d96832 100755 --- a/scripts/dts/gen_dts_rust.py +++ b/scripts/dts/gen_dts_rust.py @@ -40,48 +40,53 @@ def generate_dts_rust(edt): def generate_node_struct(node): phandle_arrays = {} + yield f'#[allow(dead_code)]\n' + yield f'#[allow(unused_parens)]\n' yield f'struct DtNode{node.dep_ordinal} {{\n' for child_name, child_node in node.children.items(): - yield f' {replace_chars(child_name)}: &DtNode{child_node.dep_ordinal},\n' + yield f' {replace_chars(child_name)}: &\'static DtNode{child_node.dep_ordinal},\n' for property_name, property_object in node.props.items(): rust_type = None + property_type = property_object.spec.type - if property_object.spec.type == 'boolean': + if property_type == 'boolean': rust_type = 'bool' - if property_object.spec.type == 'int': + if property_type == 'int': rust_type = 'u32' - if property_object.spec.type == 'string': - rust_type = 'str' - if property_object.spec.type == 'uint8-array': - rust_type = '[u8]' - if property_object.spec.type == 'array': - rust_type = '[u32]' - if property_object.spec.type == 'string-array': - rust_type = '[str]' - if property_object.spec.type in ('phandle', 'path'): - rust_type = f'&DtNode{property_object.val.dep_ordinal}' - if property_object.spec.type == 'phandles': - rust_type = '(' + ', '.join(f'&DtNode{n.dep_ordinal}' for n in property_object.val) + ')' - if property_object.spec.type == 'phandle-array': + if property_type == 'string': + rust_type = '&\'static str' + if property_type == 'uint8-array': + rust_type = f'[u8; {len(property_object.val)}]' + if property_type == 'array': + rust_type = f'[u32; {len(property_object.val)}]' + if property_type == 'string-array': + rust_type = f'[&\'static str; {len(property_object.val)}]' + if property_type in ('phandle', 'path'): + rust_type = f'&\'static DtNode{property_object.val.dep_ordinal}' + if property_type == 'phandles': + rust_type = '(' + ', '.join(f'&\'static DtNode{n.dep_ordinal}' for n in property_object.val) + ')' + if property_type == 'phandle-array': pha_types = [] for pha_entry in property_object.val: pha_type = f'DtNode{node.dep_ordinal}Pha{len(phandle_arrays)}' pha_types.append(pha_type) phandle_arrays[pha_type] = pha_entry - rust_type = '(' + ', '.join('&' + pha_id for pha_id in pha_types) + ')' + rust_type = '(' + ', '.join('&\'static ' + pha_id for pha_id in pha_types) + ')' if rust_type: yield f' {replace_chars(property_name)}: {rust_type},\n' else: - yield f' // {replace_chars(property_name)} ({property_object.spec.type})\n' + yield f' // {replace_chars(property_name)} ({property_type})\n' yield '}\n\n' for pha_type, pha_entry in phandle_arrays.items(): + yield f'#[allow(dead_code)]\n' + yield f'#[allow(unused_parens)]\n' yield f'struct {pha_type} {{\n' - yield f' controller: &DtNode{pha_entry.controller.dep_ordinal},\n' + yield f' controller: &\'static DtNode{pha_entry.controller.dep_ordinal},\n' for cell_name in pha_entry.data.keys(): yield f' {replace_chars(cell_name)}: u32,\n' yield '}\n\n' @@ -97,50 +102,54 @@ def generate_node_instance(node): if node.matching_compat: yield f'// matching_compat("{node.matching_compat}")\n' + yield f'#[allow(dead_code)]\n' + yield f'#[allow(unused_parens)]\n' yield f'const DT_NODE_{node.dep_ordinal}: DtNode{node.dep_ordinal} = DtNode{node.dep_ordinal} {{\n' for child_name, child_node in node.children.items(): - yield f' {replace_chars(child_name)}: DT_NODE_{child_node.dep_ordinal},\n' + yield f' {replace_chars(child_name)}: &DT_NODE_{child_node.dep_ordinal},\n' for property_name, property_object in node.props.items(): - if property_object.spec.type == 'boolean': - yield f' {replace_chars(property_name)}: {"true" if property_object.val else "false"},\n' - elif property_object.spec.type == 'int': - yield f' {replace_chars(property_name)}: {property_object.val},\n' - elif property_object.spec.type == 'string': - yield f' {replace_chars(property_name)}: "{property_object.val}",\n' - elif property_object.spec.type in ('uint8-array', 'array'): - yield f' {replace_chars(property_name)}: [' - yield ', '.join(str(val) for val in property_object.val) - yield '],\n' - elif property_object.spec.type == 'string-array': - yield f' {replace_chars(property_name)}: [' - yield ', '.join(f'"{val}"' for val in property_object.val) - yield '],\n' - elif property_object.spec.type in ('phandle', 'path'): - yield f' {replace_chars(property_name)}: "DT_NODE_{property_object.val.dep_ordinal}",\n' - elif property_object.spec.type == 'phandles': - yield f' {replace_chars(property_name)}: (' - yield ', '.join(f'DT_NODE_{n.dep_ordinal}' for n in property_object.val) - yield '),\n' - elif property_object.spec.type == 'phandle-array': - pha_ids = [] + property_type = property_object.spec.type + rust_value = None + + if property_type == 'boolean': + rust_value = "true" if property_object.val else "false" + elif property_type == 'int': + rust_value = str(property_object.val) + elif property_type == 'string': + rust_value = f'"{property_object.val}"' + elif property_type in ('uint8-array', 'array'): + rust_value = '[' + ', '.join(str(val) for val in property_object.val) + ']' + elif property_type == 'string-array': + rust_value = '[' + ', '.join(f'"{val}"' for val in property_object.val) + ']' + elif property_type in ('phandle', 'path'): + rust_value = f'&DT_NODE_{property_object.val.dep_ordinal}' + elif property_type == 'phandles': + rust_value = '(' + ', '.join(f'&DT_NODE_{n.dep_ordinal}' for n in property_object.val) + ')' + elif property_type == 'phandle-array': + pha_insts = [] for pha_entry in property_object.val: pha_inst = f'DT_NODE_{node.dep_ordinal}_PHA_{len(phandle_arrays)}' - pha_type = f'&DtNode{node.dep_ordinal}Pha{len(phandle_arrays)}' - pha_ids.append(pha_inst) + pha_type = f'DtNode{node.dep_ordinal}Pha{len(phandle_arrays)}' + pha_insts.append('&' + pha_inst) phandle_arrays[pha_inst] = (pha_type, pha_entry) - yield f' {replace_chars(property_name)}: (' + ', '.join(pha_ids) + '),\n' + rust_value = '(' + ', '.join(pha_insts) + ')' + + if rust_value: + yield f' {replace_chars(property_name)}: {rust_value},\n' else: - yield f' // {replace_chars(property_name)} ({property_object.spec.type})\n' + yield f' // {replace_chars(property_name)} ({property_type})\n' yield '};\n\n' for pha_inst, (pha_type, pha_entry) in phandle_arrays.items(): if pha_entry.name: yield f'// {pha_entry.name}\n' + yield f'#[allow(dead_code)]\n' + yield f'#[allow(unused_parens)]\n' yield f'const {pha_inst}: {pha_type} = {pha_type} {{\n' - yield f' controller: DT_NODE_{pha_entry.controller.dep_ordinal},\n' + yield f' controller: &DT_NODE_{pha_entry.controller.dep_ordinal},\n' for cell_name, cell_value in pha_entry.data.items(): yield f' {replace_chars(cell_name)}: {cell_value},\n' yield '};\n\n' From 74afe70d869e1ef62a49ea907dc2894141a0a0f4 Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Sat, 20 Jul 2024 08:57:37 +0200 Subject: [PATCH 34/35] make device tree accessible in application --- lib/rust/main.c | 5 -- lib/rust/zephyr/src/devicetree.rs | 12 +++ lib/rust/zephyr/src/drivers.rs | 3 +- lib/rust/zephyr/src/drivers/gpio.rs | 47 +++++++++--- lib/rust/zephyr/src/lib.rs | 2 +- samples/rust/hello_rust/src/lib.rs | 9 +-- scripts/dts/gen_dts_rust.py | 115 +++++++++++++++++++++++----- 7 files changed, 149 insertions(+), 44 deletions(-) diff --git a/lib/rust/main.c b/lib/rust/main.c index e5ba92d914d1..da6fc5548f8e 100644 --- a/lib/rust/main.c +++ b/lib/rust/main.c @@ -7,13 +7,8 @@ #ifdef CONFIG_RUST -#include -#include - extern void rust_main(void); -const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); - int main(void) { rust_main(); diff --git a/lib/rust/zephyr/src/devicetree.rs b/lib/rust/zephyr/src/devicetree.rs index 0546c921689e..7ff4cde245fd 100644 --- a/lib/rust/zephyr/src/devicetree.rs +++ b/lib/rust/zephyr/src/devicetree.rs @@ -1 +1,13 @@ include!(env!("RUST_DTS")); + +pub const fn root() -> &'static DtNode0 { &DT_NODE_0 } + +#[macro_export] +macro_rules! dt_alias { + ($alias:ident) => { $crate::devicetree::root().aliases.$alias }; +} + +#[macro_export] +macro_rules! device_dt_get { + ($node:expr) => { $node.device() }; +} diff --git a/lib/rust/zephyr/src/drivers.rs b/lib/rust/zephyr/src/drivers.rs index 39e634708b19..ae0f77798620 100644 --- a/lib/rust/zephyr/src/drivers.rs +++ b/lib/rust/zephyr/src/drivers.rs @@ -1 +1,2 @@ -pub mod gpio; \ No newline at end of file +#[cfg(CONFIG_GPIO)] +pub mod gpio; diff --git a/lib/rust/zephyr/src/drivers/gpio.rs b/lib/rust/zephyr/src/drivers/gpio.rs index 3109a384a861..a9140258d0f3 100644 --- a/lib/rust/zephyr/src/drivers/gpio.rs +++ b/lib/rust/zephyr/src/drivers/gpio.rs @@ -1,9 +1,7 @@ use bitmask_enum::bitmask; use crate::errno::{Errno, parse_result}; -extern "C" { - static led: zephyr_sys::gpio_dt_spec; -} +pub use zephyr_sys::gpio_dt_spec; #[bitmask(u32)] pub enum Flags { @@ -20,19 +18,15 @@ pub enum Flags { } pub struct Pin { - gpio_dt_spec: zephyr_sys::gpio_dt_spec, + gpio_dt_spec: gpio_dt_spec, } impl Pin { - pub fn get_led() -> Pin { - unsafe { - return Pin { - gpio_dt_spec: led.clone(), - } - } + pub fn new(gpio_dt_spec: gpio_dt_spec) -> Self { + Pin { gpio_dt_spec } } - pub fn configure(&self, extra_flags: Flags) -> Result<(), Errno>{ + pub fn configure(&self, extra_flags: Flags) -> Result<(), Errno> { unsafe { parse_result(zephyr_sys::gpio_pin_configure_dt(&self.gpio_dt_spec, extra_flags.bits())) } @@ -44,3 +38,34 @@ impl Pin { } } } + +#[macro_export] +macro_rules! dt_gpio_ctrl_by_idx { + ($node:expr, $prop:ident, $idx:tt) => { $node.$prop.$idx.phandle } +} + +#[macro_export] +macro_rules! dt_gpio_pin_by_idx { + ($node:expr, $prop:ident, $idx:tt) => { $node.$prop.$idx.cell_pin }; +} + +#[macro_export] +macro_rules! dt_gpio_flags_by_idx { + ($node:expr, $prop:ident, $idx:tt) => { $node.$prop.$idx.cell_flags }; +} + +#[macro_export] +macro_rules! gpio_dt_spec_get_by_idx { + ($node:expr, $prop:ident, $idx:tt) => { + crate::drivers::gpio::gpio_dt_spec { + port: device_dt_get!(dt_gpio_ctrl_by_idx!($node, $prop, $idx)), + pin: dt_gpio_pin_by_idx!($node, $prop, $idx) as u8, + dt_flags: dt_gpio_flags_by_idx!($node, $prop, $idx) as u16, + } + } +} + +#[macro_export] +macro_rules! gpio_dt_spec_get { + ($node:expr, $prop:ident) => { gpio_dt_spec_get_by_idx!($node, $prop, 0) } +} diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index 5d1fa32a0281..ae732788f710 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -12,12 +12,12 @@ extern crate alloc; mod allocator; mod panic; -mod devicetree; pub mod errno; pub mod kernel; pub mod print; pub mod drivers; +pub mod devicetree; // Bring in the generated kconfig module include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); diff --git a/samples/rust/hello_rust/src/lib.rs b/samples/rust/hello_rust/src/lib.rs index ea94b7917902..c31d02e4586a 100644 --- a/samples/rust/hello_rust/src/lib.rs +++ b/samples/rust/hello_rust/src/lib.rs @@ -8,20 +8,19 @@ extern crate zephyr; extern crate alloc; -use zephyr::kernel::msleep; +use zephyr::*; use zephyr::drivers::gpio; -use zephyr::println; #[no_mangle] extern "C" fn rust_main() { - println!("Hello, world! {}", zephyr::kconfig::CONFIG_BOARD); + println!("Hello, world! {}", kconfig::CONFIG_BOARD); - let pin = gpio::Pin::get_led(); + let pin = gpio::Pin::new(gpio_dt_spec_get!(dt_alias!(led0), gpios)); pin.configure(gpio::Flags::OutputActive).expect("Failed to configure pin."); loop { - msleep(1000); + kernel::msleep(1000); pin.toggle().expect("Failed to toggle pin."); } } diff --git a/scripts/dts/gen_dts_rust.py b/scripts/dts/gen_dts_rust.py index 230375d96832..f0cd9fbc8c35 100755 --- a/scripts/dts/gen_dts_rust.py +++ b/scripts/dts/gen_dts_rust.py @@ -31,26 +31,62 @@ def main(): def generate_dts_rust(edt): for node in edt.nodes: - yield from generate_node_struct(node) + if node.path == '/chosen': + yield from generate_chosen_node_struct(node) + elif node.path == '/aliases': + yield from generate_aliases_node_struct(node) + else: + yield from generate_normal_node_struct(node) for node in edt.nodes: - yield from generate_node_instance(node) + if node.path == '/chosen': + yield from generate_chosen_node_instance(node) + elif node.path == '/aliases': + yield from generate_aliases_node_instance(node) + else: + yield from generate_normal_node_instance(node) + + +def generate_chosen_node_struct(node): + yield f'#[allow(dead_code)]\n' + yield f'pub struct DtNode{node.dep_ordinal} {{\n' + for chosen_name, chosen_node in node.edt.chosen_nodes.items(): + yield f' pub {replace_chars(chosen_name)}: &\'static DtNode{chosen_node.dep_ordinal},\n' -def generate_node_struct(node): + yield '}\n\n' + + +def generate_aliases_node_struct(node): + yield f'#[allow(dead_code)]\n' + yield f'pub struct DtNode{node.dep_ordinal} {{\n' + + alias2node = {} + for node in node.edt.nodes: + for name in node.aliases: + alias2node[name] = node + + for alias_name, alias_node in alias2node.items(): + yield f' pub {replace_chars(alias_name)}: &\'static DtNode{alias_node.dep_ordinal},\n' + + yield '}\n\n' + + +def generate_normal_node_struct(node): phandle_arrays = {} yield f'#[allow(dead_code)]\n' - yield f'#[allow(unused_parens)]\n' - yield f'struct DtNode{node.dep_ordinal} {{\n' + yield f'pub struct DtNode{node.dep_ordinal} {{\n' for child_name, child_node in node.children.items(): - yield f' {replace_chars(child_name)}: &\'static DtNode{child_node.dep_ordinal},\n' + yield f' pub {replace_chars(child_name)}: &\'static DtNode{child_node.dep_ordinal},\n' for property_name, property_object in node.props.items(): rust_type = None property_type = property_object.spec.type + if property_name == 'status': + continue if property_type == 'boolean': rust_type = 'bool' if property_type == 'int': @@ -66,44 +102,71 @@ def generate_node_struct(node): if property_type in ('phandle', 'path'): rust_type = f'&\'static DtNode{property_object.val.dep_ordinal}' if property_type == 'phandles': - rust_type = '(' + ', '.join(f'&\'static DtNode{n.dep_ordinal}' for n in property_object.val) + ')' + rust_type = '(' + ', '.join(f'&\'static DtNode{n.dep_ordinal}' for n in property_object.val) + ',)' if property_type == 'phandle-array': pha_types = [] for pha_entry in property_object.val: pha_type = f'DtNode{node.dep_ordinal}Pha{len(phandle_arrays)}' pha_types.append(pha_type) phandle_arrays[pha_type] = pha_entry - rust_type = '(' + ', '.join('&\'static ' + pha_id for pha_id in pha_types) + ')' + rust_type = '(' + ', '.join('&\'static ' + pha_id for pha_id in pha_types) + ',)' if rust_type: - yield f' {replace_chars(property_name)}: {rust_type},\n' + yield f' pub {replace_chars(property_name)}: {rust_type},\n' else: yield f' // {replace_chars(property_name)} ({property_type})\n' + yield ' pub status: &\'static str,\n' yield '}\n\n' for pha_type, pha_entry in phandle_arrays.items(): yield f'#[allow(dead_code)]\n' - yield f'#[allow(unused_parens)]\n' - yield f'struct {pha_type} {{\n' - yield f' controller: &\'static DtNode{pha_entry.controller.dep_ordinal},\n' + yield f'pub struct {pha_type} {{\n' + yield f' pub phandle: &\'static DtNode{pha_entry.controller.dep_ordinal},\n' for cell_name in pha_entry.data.keys(): - yield f' {replace_chars(cell_name)}: u32,\n' + yield f' pub cell_{replace_chars(cell_name)}: u32,\n' yield '}\n\n' -def generate_node_instance(node): +def generate_chosen_node_instance(node): + yield f'// path("{node.path}")\n' + yield f'#[allow(dead_code)]\n' + yield f'const DT_NODE_{node.dep_ordinal}: DtNode{node.dep_ordinal} = DtNode{node.dep_ordinal} {{\n' + + for chosen_name, chosen_node in node.edt.chosen_nodes.items(): + yield f' {replace_chars(chosen_name)}: &DT_NODE_{chosen_node.dep_ordinal},\n' + + yield '};\n\n' + + +def generate_aliases_node_instance(node): + yield f'// path("{node.path}")\n' + yield f'#[allow(dead_code)]\n' + yield f'const DT_NODE_{node.dep_ordinal}: DtNode{node.dep_ordinal} = DtNode{node.dep_ordinal} {{\n' + + alias2node = {} + for node in node.edt.nodes: + for name in node.aliases: + alias2node[name] = node + + for alias_name, alias_node in alias2node.items(): + yield f' {replace_chars(alias_name)}: &DT_NODE_{alias_node.dep_ordinal},\n' + + yield '};\n\n' + + +def generate_normal_node_instance(node): phandle_arrays = {} + yield f'// path("{node.path}")\n' for label in node.labels: - yield f'// label("{label}")\n' + yield f'// nodelabel("{label}")\n' for alias in node.aliases: yield f'// alias("{alias}")\n' if node.matching_compat: yield f'// matching_compat("{node.matching_compat}")\n' yield f'#[allow(dead_code)]\n' - yield f'#[allow(unused_parens)]\n' yield f'const DT_NODE_{node.dep_ordinal}: DtNode{node.dep_ordinal} = DtNode{node.dep_ordinal} {{\n' for child_name, child_node in node.children.items(): @@ -113,6 +176,8 @@ def generate_node_instance(node): property_type = property_object.spec.type rust_value = None + if property_name == 'status': + continue if property_type == 'boolean': rust_value = "true" if property_object.val else "false" elif property_type == 'int': @@ -126,7 +191,7 @@ def generate_node_instance(node): elif property_type in ('phandle', 'path'): rust_value = f'&DT_NODE_{property_object.val.dep_ordinal}' elif property_type == 'phandles': - rust_value = '(' + ', '.join(f'&DT_NODE_{n.dep_ordinal}' for n in property_object.val) + ')' + rust_value = '(' + ', '.join(f'&DT_NODE_{n.dep_ordinal}' for n in property_object.val) + ',)' elif property_type == 'phandle-array': pha_insts = [] for pha_entry in property_object.val: @@ -134,26 +199,34 @@ def generate_node_instance(node): pha_type = f'DtNode{node.dep_ordinal}Pha{len(phandle_arrays)}' pha_insts.append('&' + pha_inst) phandle_arrays[pha_inst] = (pha_type, pha_entry) - rust_value = '(' + ', '.join(pha_insts) + ')' + rust_value = '(' + ', '.join(pha_insts) + ',)' if rust_value: yield f' {replace_chars(property_name)}: {rust_value},\n' else: yield f' // {replace_chars(property_name)} ({property_type})\n' + yield f' status: "{node.status}",\n' yield '};\n\n' for pha_inst, (pha_type, pha_entry) in phandle_arrays.items(): if pha_entry.name: yield f'// {pha_entry.name}\n' yield f'#[allow(dead_code)]\n' - yield f'#[allow(unused_parens)]\n' yield f'const {pha_inst}: {pha_type} = {pha_type} {{\n' - yield f' controller: &DT_NODE_{pha_entry.controller.dep_ordinal},\n' + yield f' phandle: &DT_NODE_{pha_entry.controller.dep_ordinal},\n' for cell_name, cell_value in pha_entry.data.items(): - yield f' {replace_chars(cell_name)}: {cell_value},\n' + yield f' cell_{replace_chars(cell_name)}: {cell_value},\n' yield '};\n\n' + if node.status == 'okay': + yield f'#[allow(dead_code)]\n' + yield f'impl DtNode{node.dep_ordinal} {{\n' + yield ' pub fn device(&self) -> *const zephyr_sys::device {\n' + yield f' unsafe {{ &zephyr_sys::__device_dts_ord_{node.dep_ordinal} }}\n' + yield ' }\n' + yield '}\n\n' + def replace_chars(text: str): return re.sub(r'\W+', '_', text) From dea56463ebbf469118ed34ee5c9e343586bf27da Mon Sep 17 00:00:00 2001 From: Mario Jaun Date: Tue, 23 Jul 2024 09:30:41 +0200 Subject: [PATCH 35/35] cleanups --- cmake/modules/dts.cmake | 21 --------------------- cmake/modules/rust.cmake | 29 +++++++++++++++++++++++------ lib/rust/panic.c | 6 +++++- lib/rust/zephyr-sys/Cargo.toml | 3 +++ lib/rust/zephyr-sys/bindgen_input.h | 3 +++ lib/rust/zephyr-sys/build.rs | 14 +++++++++++--- lib/rust/zephyr-sys/src/lib.rs | 3 +++ lib/rust/zephyr/Cargo.toml | 2 ++ lib/rust/zephyr/src/allocator.rs | 3 +++ lib/rust/zephyr/src/devicetree.rs | 3 +++ lib/rust/zephyr/src/drivers.rs | 3 +++ lib/rust/zephyr/src/drivers/gpio.rs | 29 +++++++++++++++++------------ lib/rust/zephyr/src/errno.rs | 25 +++++++++++++++---------- lib/rust/zephyr/src/kernel.rs | 26 +++++++++++++++++++++++++- lib/rust/zephyr/src/lib.rs | 5 ++++- lib/rust/zephyr/src/panic.rs | 7 +++++-- lib/rust/zephyr/src/print.rs | 22 ---------------------- samples/rust/hello_rust/prj.conf | 5 ++++- samples/rust/hello_rust/src/lib.rs | 18 +++++++++--------- scripts/dts/gen_dts_rust.py | 16 ++++++++++++++++ 20 files changed, 154 insertions(+), 89 deletions(-) delete mode 100644 lib/rust/zephyr/src/print.rs diff --git a/cmake/modules/dts.cmake b/cmake/modules/dts.cmake index 15559a04a733..ece7be588a31 100644 --- a/cmake/modules/dts.cmake +++ b/cmake/modules/dts.cmake @@ -118,11 +118,6 @@ set(GEN_DTS_CMAKE_SCRIPT ${DT_SCRIPTS}/gen_dts_cmake.py) # creating it. set(DTS_CMAKE ${PROJECT_BINARY_DIR}/dts.cmake) -# This generates DT information needed by Rust applications. -set(GEN_DTS_RUST_SCRIPT ${DT_SCRIPTS}/gen_dts_rust.py) -# The generated information itself, which is further processed by the Rust build script. -set(DTS_RUST ${PROJECT_BINARY_DIR}/dts.rs) - # The location of a file containing known vendor prefixes, relative to # each element of DTS_ROOT. Users can define their own in their own # modules. @@ -274,7 +269,6 @@ set_property(DIRECTORY APPEND PROPERTY ${GEN_DEFINES_SCRIPT} ${GEN_DRIVER_KCONFIG_SCRIPT} ${GEN_DTS_CMAKE_SCRIPT} - ${GEN_DTS_RUST_SCRIPT} ) # @@ -341,21 +335,6 @@ else() include(${DTS_CMAKE}) endif() -# -# Run GEN_DTS_RUST_SCRIPT. -# - -execute_process( - COMMAND ${PYTHON_EXECUTABLE} ${GEN_DTS_RUST_SCRIPT} - --edt-pickle ${EDT_PICKLE} - --rust-out ${DTS_RUST} - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - RESULT_VARIABLE ret -) -if(NOT "${ret}" STREQUAL "0") - message(FATAL_ERROR "gen_dts_rust.py failed with return code: ${ret}") -endif() - # # Run dtc if it was found. # diff --git a/cmake/modules/rust.cmake b/cmake/modules/rust.cmake index 58b5ed670cf2..6e24f1b197f5 100644 --- a/cmake/modules/rust.cmake +++ b/cmake/modules/rust.cmake @@ -45,14 +45,16 @@ function(_generate_clang_args BINDGEN_CLANG_ARGS) zephyr_get_include_directories_for_lang(C includes) zephyr_get_compile_definitions_for_lang(C definitions) - # Gather -imacros options + # -imacros are needed but are part of zephyr_get_compile_options_for_lang() where many + # things are not supported by Clang. Maybe there is a better way than hard coding. set(options "-imacros${AUTOCONF_H}") if(CONFIG_ENFORCE_ZEPHYR_STDINT) list(APPEND options "-imacros${ZEPHYR_BASE}/include/zephyr/toolchain/zephyr_stdint.h") endif() - # Determine standard include directories of compiler + # Determine standard include directories of compiler. + # I hope someone knows a nicer way of doing this. file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/empty.c) execute_process( @@ -73,7 +75,9 @@ function(_generate_clang_args BINDGEN_CLANG_ARGS) message(WARNING "Unable to determine compiler standard include directories.") endif() - # Generate file containing arguments for clang. Note that the file is generated after the + # Not sure if a proper target should be provided as well to generate the correct bindings. + + # Generate file containing arguments for Clang. Note that the file is generated after the # CMake configure stage as the variables contain generator expressions which cannot be # evaluated right now. file( @@ -83,6 +87,17 @@ function(_generate_clang_args BINDGEN_CLANG_ARGS) ) endfunction() +function(_generate_rust_dts RUST_DTS) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_dts_rust.py + --edt-pickle ${CMAKE_BINARY_DIR}/zephyr/edt.pickle + --rust-out ${RUST_DTS} + COMMAND_ERROR_IS_FATAL ANY + ) + + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${GEN_DTS_RUST_SCRIPT}) +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 @@ -102,12 +117,14 @@ function(rust_cargo_application) 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") - set(RUST_DTS "${CMAKE_BINARY_DIR}/zephyr/dts.rs") - + set(RUST_DTS "${CMAKE_CURRENT_BINARY_DIR}/rust/dts.rs") set(BINDGEN_CLANG_ARGS "${CMAKE_CURRENT_BINARY_DIR}/rust/clang_args.txt") - set(BINDGEN_WRAP_STATIC_FNS "${CARGO_TARGET_DIR}/${RUST_TARGET}/${RUST_BUILD_TYPE}/wrap_static_fns.c") + set(BINDGEN_WRAP_STATIC_FNS "${CMAKE_CURRENT_BINARY_DIR}/rust/wrap_static_fns.c") + + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rust") _generate_clang_args(${BINDGEN_CLANG_ARGS}) + _generate_rust_dts(${RUST_DTS}) # 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 diff --git a/lib/rust/panic.c b/lib/rust/panic.c index 642b983ce5fb..b398b9b52226 100644 --- a/lib/rust/panic.c +++ b/lib/rust/panic.c @@ -1,6 +1,10 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + #include -void k_panic__extern() +// Provide a symbol to call from Rust +void rust_panic() { k_panic(); } diff --git a/lib/rust/zephyr-sys/Cargo.toml b/lib/rust/zephyr-sys/Cargo.toml index f33c729c7cba..db22cbf08c21 100644 --- a/lib/rust/zephyr-sys/Cargo.toml +++ b/lib/rust/zephyr-sys/Cargo.toml @@ -1,3 +1,6 @@ +# Copyright (c) 2024 Zühlke Engineering AG +# SPDX-License-Identifier: Apache-2.0 + [package] name = "zephyr-sys" version = "0.1.0" diff --git a/lib/rust/zephyr-sys/bindgen_input.h b/lib/rust/zephyr-sys/bindgen_input.h index 3e6a76154b99..d0a827acb881 100644 --- a/lib/rust/zephyr-sys/bindgen_input.h +++ b/lib/rust/zephyr-sys/bindgen_input.h @@ -1,3 +1,6 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + #include #include #include diff --git a/lib/rust/zephyr-sys/build.rs b/lib/rust/zephyr-sys/build.rs index 3952364cd2bd..7bbe7b58ee6e 100644 --- a/lib/rust/zephyr-sys/build.rs +++ b/lib/rust/zephyr-sys/build.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + use std::{env, fs}; use std::path::PathBuf; @@ -15,6 +18,10 @@ fn main() { .detect_include_paths(false) .wrap_static_fns(true) .wrap_static_fns_path(wrap_static_fns) + .clang_args(clang_args.split(';')) + .header(input_header.to_str().unwrap()) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .allowlist_file(zephyr_base.join(".*").to_str().unwrap()) .allowlist_file(".*/errno.h") .blocklist_function("z_impl_.*") @@ -22,9 +29,10 @@ fn main() { .blocklist_var("DT_.*") .blocklist_var("CONFIG_.*") .blocklist_var("Z_UTIL_.*") - .clang_args(clang_args.split(';')) - .header(input_header.to_str().unwrap()) - .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + + // Deprecated function, hopefully there is a more generic way of doing this. + .blocklist_function("sys_clock_timeout_end_calc") + .generate() .expect("Unable to generate bindings!"); diff --git a/lib/rust/zephyr-sys/src/lib.rs b/lib/rust/zephyr-sys/src/lib.rs index 9c84cdfcc21b..19b04b206a6a 100644 --- a/lib/rust/zephyr-sys/src/lib.rs +++ b/lib/rust/zephyr-sys/src/lib.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + #![no_std] #![allow(non_upper_case_globals)] diff --git a/lib/rust/zephyr/Cargo.toml b/lib/rust/zephyr/Cargo.toml index 28259d5a5afd..2f9b8d7b6237 100644 --- a/lib/rust/zephyr/Cargo.toml +++ b/lib/rust/zephyr/Cargo.toml @@ -11,6 +11,8 @@ Functionality for Rust-based applications that run on Zephyr. [dependencies] zephyr-sys = "0.1.0" + +# Just for this experiment, probably the zephyr crate should come without dependencies. bitmask-enum = "2.2.4" int-enum = "1.1.2" diff --git a/lib/rust/zephyr/src/allocator.rs b/lib/rust/zephyr/src/allocator.rs index ed1623029f2d..aff753e49a42 100644 --- a/lib/rust/zephyr/src/allocator.rs +++ b/lib/rust/zephyr/src/allocator.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + use core::alloc::{GlobalAlloc, Layout}; use zephyr_sys::{k_aligned_alloc, k_free}; diff --git a/lib/rust/zephyr/src/devicetree.rs b/lib/rust/zephyr/src/devicetree.rs index 7ff4cde245fd..451526012f82 100644 --- a/lib/rust/zephyr/src/devicetree.rs +++ b/lib/rust/zephyr/src/devicetree.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + include!(env!("RUST_DTS")); pub const fn root() -> &'static DtNode0 { &DT_NODE_0 } diff --git a/lib/rust/zephyr/src/drivers.rs b/lib/rust/zephyr/src/drivers.rs index ae0f77798620..e9c1284f66fd 100644 --- a/lib/rust/zephyr/src/drivers.rs +++ b/lib/rust/zephyr/src/drivers.rs @@ -1,2 +1,5 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + #[cfg(CONFIG_GPIO)] pub mod gpio; diff --git a/lib/rust/zephyr/src/drivers/gpio.rs b/lib/rust/zephyr/src/drivers/gpio.rs index a9140258d0f3..febdd5de4c2d 100644 --- a/lib/rust/zephyr/src/drivers/gpio.rs +++ b/lib/rust/zephyr/src/drivers/gpio.rs @@ -1,10 +1,15 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + use bitmask_enum::bitmask; -use crate::errno::{Errno, parse_result}; +use crate::errno::Errno; -pub use zephyr_sys::gpio_dt_spec; +// Maybe this should be somehow wrapped to not expose zephyr_sys stuff to the application. +pub type GpioDtSpec = zephyr_sys::gpio_dt_spec; #[bitmask(u32)] -pub enum Flags { +pub enum GpioFlags { + None = 0, Input = zephyr_sys::GPIO_INPUT, Output = zephyr_sys::GPIO_OUTPUT, Disconnected = zephyr_sys::GPIO_DISCONNECTED, @@ -17,24 +22,24 @@ pub enum Flags { OutputActive = zephyr_sys::GPIO_OUTPUT | zephyr_sys::GPIO_OUTPUT_INIT_HIGH | zephyr_sys::GPIO_OUTPUT_INIT_LOGICAL, } -pub struct Pin { - gpio_dt_spec: gpio_dt_spec, +pub struct GpioPin { + gpio_dt_spec: GpioDtSpec, } -impl Pin { - pub fn new(gpio_dt_spec: gpio_dt_spec) -> Self { - Pin { gpio_dt_spec } +impl GpioPin { + pub fn new(gpio_dt_spec: GpioDtSpec) -> Self { + GpioPin { gpio_dt_spec } } - pub fn configure(&self, extra_flags: Flags) -> Result<(), Errno> { + pub fn configure(&self, extra_flags: GpioFlags) -> Result<(), Errno> { unsafe { - parse_result(zephyr_sys::gpio_pin_configure_dt(&self.gpio_dt_spec, extra_flags.bits())) + Errno::from(zephyr_sys::gpio_pin_configure_dt(&self.gpio_dt_spec, extra_flags.bits())) } } pub fn toggle(&self) -> Result<(), Errno> { unsafe { - parse_result(zephyr_sys::gpio_pin_toggle_dt(&self.gpio_dt_spec)) + Errno::from(zephyr_sys::gpio_pin_toggle_dt(&self.gpio_dt_spec)) } } } @@ -57,7 +62,7 @@ macro_rules! dt_gpio_flags_by_idx { #[macro_export] macro_rules! gpio_dt_spec_get_by_idx { ($node:expr, $prop:ident, $idx:tt) => { - crate::drivers::gpio::gpio_dt_spec { + crate::drivers::gpio::GpioDtSpec { port: device_dt_get!(dt_gpio_ctrl_by_idx!($node, $prop, $idx)), pin: dt_gpio_pin_by_idx!($node, $prop, $idx) as u8, dt_flags: dt_gpio_flags_by_idx!($node, $prop, $idx) as u16, diff --git a/lib/rust/zephyr/src/errno.rs b/lib/rust/zephyr/src/errno.rs index 37a1106981d8..b3e4c3213711 100644 --- a/lib/rust/zephyr/src/errno.rs +++ b/lib/rust/zephyr/src/errno.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + use int_enum::IntEnum; #[repr(u32)] @@ -84,17 +87,19 @@ pub enum Errno { ECANCELED = zephyr_sys::ECANCELED, } -pub fn parse_result(result: i32) -> Result<(), Errno> { - parse_result_with_value(result).map(|_| ()) -} - -pub fn parse_result_with_value(result: i32) -> Result { - if result >= 0 { - return Ok(result) +impl Errno { + pub fn from(result: core::ffi::c_int) -> Result<(), Self> { + Self::from_result(result).map(|_| ()) } - match Errno::try_from(-result as u32) { - Ok(errno) => Err(errno), - _ => panic!("Unexpected value"), + pub fn from_result(result: core::ffi::c_int) -> Result { + if result >= 0 { + return Ok(result as i32); + } + + match Self::try_from(-result as u32) { + Ok(errno) => Err(errno), + _ => panic!("Unexpected value"), + } } } diff --git a/lib/rust/zephyr/src/kernel.rs b/lib/rust/zephyr/src/kernel.rs index f9f7803e29a9..83234be808cb 100644 --- a/lib/rust/zephyr/src/kernel.rs +++ b/lib/rust/zephyr/src/kernel.rs @@ -1,3 +1,27 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + +use core::ffi::c_char; +use alloc::ffi::CString; + +#[macro_export] +macro_rules! printk { + ($msg:expr) => { + crate::kernel::printk($msg); + }; + ($fmt:expr, $($arg:tt)*) => { + crate::kernel::printk(::alloc::format!($fmt, $($arg)*).as_str()); + }; +} + +pub fn printk(msg: &str) { + let cstring = CString::new(msg).unwrap(); + + unsafe { + zephyr_sys::printk("%s\0".as_ptr() as *const c_char, cstring.as_ptr() as *const c_char); + } +} + pub fn msleep(ms: i32) -> i32 { unsafe { zephyr_sys::k_msleep(ms) } -} \ No newline at end of file +} diff --git a/lib/rust/zephyr/src/lib.rs b/lib/rust/zephyr/src/lib.rs index ae732788f710..fc030ff6da4e 100644 --- a/lib/rust/zephyr/src/lib.rs +++ b/lib/rust/zephyr/src/lib.rs @@ -15,10 +15,13 @@ mod panic; pub mod errno; pub mod kernel; -pub mod print; pub mod drivers; pub mod devicetree; +// To me, it would feel more consistent if kconfig.rs was also generated using Python within the +// Zephyr build infrastructure instead of the zephyr-build crate. For the devicetree this was +// much easier as there is already the pickled EDT available. + // Bring in the generated kconfig module include!(concat!(env!("OUT_DIR"), "/kconfig.rs")); diff --git a/lib/rust/zephyr/src/panic.rs b/lib/rust/zephyr/src/panic.rs index 05a0d0a01aff..1457b66c3103 100644 --- a/lib/rust/zephyr/src/panic.rs +++ b/lib/rust/zephyr/src/panic.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2024 Zühlke Engineering AG +// SPDX-License-Identifier: Apache-2.0 + use alloc::ffi::CString; use alloc::string::ToString; use core::ffi::c_char; @@ -5,7 +8,7 @@ use core::panic::PanicInfo; use zephyr_sys::printk; extern "C" { - fn k_panic__extern() -> !; + fn rust_panic() -> !; } #[panic_handler] @@ -14,5 +17,5 @@ unsafe fn panic(_info: &PanicInfo) -> ! { printk("%s\n\0".as_ptr() as *const c_char, message.as_ptr()); } - k_panic__extern(); + rust_panic(); } diff --git a/lib/rust/zephyr/src/print.rs b/lib/rust/zephyr/src/print.rs deleted file mode 100644 index 62d9d7e11129..000000000000 --- a/lib/rust/zephyr/src/print.rs +++ /dev/null @@ -1,22 +0,0 @@ -use core::ffi::c_char; -use alloc::ffi::CString; -use zephyr_sys::printk; - -#[macro_export] -macro_rules! println { - ($msg:expr) => { - ::zephyr::print::println_helper($msg); - }; - ($fmt:expr, $($arg:tt)*) => { - let formatted = ::alloc::format!($fmt, $($arg)*); - ::zephyr::print::println_helper(formatted.as_str()); - }; -} - -pub fn println_helper(msg: &str) { - let cstring = CString::new(msg).unwrap(); - - unsafe { - printk("%s\n\0".as_ptr() as *const c_char, cstring.as_ptr() as *const c_char); - } -} diff --git a/samples/rust/hello_rust/prj.conf b/samples/rust/hello_rust/prj.conf index f39edba40263..5af4669365c7 100644 --- a/samples/rust/hello_rust/prj.conf +++ b/samples/rust/hello_rust/prj.conf @@ -3,5 +3,8 @@ CONFIG_RUST=y -CONFIG_MAIN_STACK_SIZE=32768 +# Seems necessary for exception unwinding +CONFIG_CPP=y + +CONFIG_MAIN_STACK_SIZE=8192 CONFIG_HEAP_MEM_POOL_SIZE=32768 diff --git a/samples/rust/hello_rust/src/lib.rs b/samples/rust/hello_rust/src/lib.rs index c31d02e4586a..9aed2603d755 100644 --- a/samples/rust/hello_rust/src/lib.rs +++ b/samples/rust/hello_rust/src/lib.rs @@ -3,24 +3,24 @@ #![no_std] -// Reference the Zephyr crate so that the panic handler gets used. This is only needed if no -// symbols from the crate are directly used. -extern crate zephyr; extern crate alloc; -use zephyr::*; -use zephyr::drivers::gpio; +use zephyr::*; // Wildcard import to bring in nested macros, maybe there is a better way +use zephyr::drivers::gpio::{GpioPin, GpioFlags}; #[no_mangle] extern "C" fn rust_main() { - println!("Hello, world! {}", kconfig::CONFIG_BOARD); + printk!("Hello, world! {}\n", kconfig::CONFIG_BOARD); - let pin = gpio::Pin::new(gpio_dt_spec_get!(dt_alias!(led0), gpios)); + let gpio_pin = GpioPin::new(gpio_dt_spec_get!(dt_alias!(led0), gpios)); - pin.configure(gpio::Flags::OutputActive).expect("Failed to configure pin."); + gpio_pin.configure(GpioFlags::OutputActive) + .expect("Failed to configure pin."); loop { kernel::msleep(1000); - pin.toggle().expect("Failed to toggle pin."); + + gpio_pin.toggle() + .expect("Failed to toggle pin."); } } diff --git a/scripts/dts/gen_dts_rust.py b/scripts/dts/gen_dts_rust.py index f0cd9fbc8c35..a5adda4b9e9e 100755 --- a/scripts/dts/gen_dts_rust.py +++ b/scripts/dts/gen_dts_rust.py @@ -1,5 +1,21 @@ #!/usr/bin/env python3 +# Copyright (c) 2024 Zühlke Engineering AG +# SPDX-License-Identifier: Apache-2.0 + +# This script uses edtlib and the devicetree data in the build directory +# to generate a Rust source file which contains devicetree data. +# +# For each node, a struct is defined and instantiated once as a constant +# containing all information and referencing child nodes and other +# nodes via phandles. For nodes with status "okay" a device() method +# is generated allowing to access the device object which can be used +# to interface with the device drivers. +# +# The provided information allows to define macros which behave similar +# as the macros we have in C to access devicetree information such as +# `GPIO_DT_SPEC_GET`. + import re import os import sys