Skip to content

Device Tree Support #15

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3bca48e
rust: zephyr-build: Conversion of Device Tree
d3zd3z Jul 18, 2024
0b7f377
Create blinky application
d3zd3z Sep 16, 2024
1139afb
zephyr: sys: Create wrappers for gpio and flash
d3zd3z Sep 16, 2024
85bfbde
zephyr: sys: gpio: Add is_ready method
d3zd3z Sep 16, 2024
0104c33
zephyr: sys: gpio: Add configure and pin toggle
d3zd3z Sep 16, 2024
09b58d1
Eliminate warnings in the auto-generated device tree
d3zd3z Oct 16, 2024
206bfe7
zephyr-build: YAML-based augment code
d3zd3z Oct 18, 2024
129332a
zephyr: Add unsafe constructor to device types
d3zd3z Oct 18, 2024
a0ae726
zephyr: Enforce uniqueness on device tree wrappers
d3zd3z Oct 18, 2024
70ed3b7
zephyr: Make gpio interface much more "unsafe"
d3zd3z Oct 18, 2024
ed06d97
zephyr-build: devicetree: Move augments into module
d3zd3z Oct 18, 2024
ba60ed2
zephyr-build: Include DT node presence configs
d3zd3z Oct 18, 2024
f6b5e89
samples: blinky: Domonstrate conditional DT compilation
d3zd3z Oct 18, 2024
b1fe3ea
zephyr: device: Split gpio and flash to own files
d3zd3z Oct 18, 2024
2dce5d5
platforms: Remove mps2
d3zd3z Oct 25, 2024
08ee474
zephyr: gpio/flash: Allow constructor to be unused
d3zd3z Oct 29, 2024
bf03633
dt-rust: Add the nrf51 flash controller
d3zd3z Oct 29, 2024
ec45675
zephyr-sys: Bump to newer bindgen
d3zd3z Oct 29, 2024
cf0d2b9
zephyr-build: Use parsed DT for both uses
d3zd3z Oct 29, 2024
64f0eb6
samples: blink: Fix for upstream API changes
d3zd3z Nov 22, 2024
d10c418
zephyr: sys: device: gpio: Allow Gpio::new to be unused
d3zd3z Dec 18, 2024
949b03e
Various cleanups from github comments
d3zd3z Dec 18, 2024
19ad114
zephyr: device: Use AtomicBool instead of AtomicUsize
d3zd3z Dec 18, 2024
37e5502
Numerous minor fixes from github reviews
d3zd3z Dec 18, 2024
64864c6
CMakeLists: Add missing decls added to doc generation
d3zd3z Dec 18, 2024
1b02946
zephyr: device: gpio: Add Send to Gpio and GpioPin
d3zd3z Dec 30, 2024
89958c3
zephyr-build: devicetree: Clean up compatable check
d3zd3z Jan 14, 2025
f655285
samples: blinky: Minor cleanups
d3zd3z Jan 14, 2025
6943226
samples: blink: Update dependency version
d3zd3z Jan 14, 2025
fe2cc40
samples: blinky: Remove user thread test
d3zd3z Jan 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

set(RUST_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "")

# Initially, we just have a single DT augment file.
set(DT_AUGMENTS "${CMAKE_CURRENT_LIST_DIR}/dt-rust.yaml" CACHE INTERNAL "")

# Zephyr targets are defined through Kconfig. We need to map these to
# an appropriate llvm target triple. This sets `RUST_TARGET` in the
# parent scope, or an error if the target is not yet supported by
Expand Down Expand Up @@ -138,6 +141,8 @@ ZEPHYR_DTS = \"${ZEPHYR_DTS}\"
INCLUDE_DIRS = \"${include_dirs}\"
INCLUDE_DEFINES = \"${include_defines}\"
WRAPPER_FILE = \"${WRAPPER_FILE}\"
BINARY_DIR_INCLUDE_GENERATED = \"${BINARY_DIR_INCLUDE_GENERATED}\"
DT_AUGMENTS = \"${DT_AUGMENTS}\"

[patch.crates-io]
${config_paths}
Expand All @@ -161,6 +166,8 @@ ${config_paths}
INCLUDE_DIRS="${include_dirs}"
INCLUDE_DEFINES="${include_defines}"
WRAPPER_FILE="${WRAPPER_FILE}"
DT_AUGMENTS="${DT_AUGMENTS}"
BINARY_DIR_INCLUDE_GENERATED="${BINARY_DIR_INCLUDE_GENERATED}"
cargo build
${rust_build_type_arg}

Expand Down Expand Up @@ -201,6 +208,8 @@ ${config_paths}
INCLUDE_DIRS="${include_dirs}"
INCLUDE_DEFINES="${include_defines}"
WRAPPER_FILE="${WRAPPER_FILE}"
DT_AUGMENTS="${DT_AUGMENTS}"
BINARY_DIR_INCLUDE_GENERATED="${BINARY_DIR_INCLUDE_GENERATED}"
cargo doc
${rust_build_type_arg}

Expand Down
87 changes: 87 additions & 0 deletions dt-rust.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Description of how to augment the devicetree for Rust.
#
# Each entry describes an augmentation that will be added to matching nodes in the device tree.
# The full syntax is described (indirectly) in `zephyr-build/src/devicetree/config.rs`.

# Gpio controllers match for every node that has a `gpio-controller` property. This is one of the
# few instances were we can actually just match on a property.
- name: gpio-controller
rules:
- type: has_prop
value: gpio-controller
actions:
- type: instance
value:
raw:
type: myself
device: crate::device::gpio::Gpio

# The gpio-leds node will have #children nodes describing each led. We'll match on the parent
# having this compatible property. The nodes themselves are built out of the properties associated
# with each gpio.
- name: gpio-leds
rules:
- type: compatible
value:
names:
- gpio-leds
level: 1
actions:
- type: instance
value:
raw:
type: phandle
value: gpios
device: crate::device::gpio::GpioPin

# Flash controllers don't have any particular property to identify them, so we need a list of
# compatible values that should match.
- name: flash-controller
rules:
- type: compatible
value:
names:
- "nordic,nrf52-flash-controller"
- "nordic,nrf51-flash-controller"
- "raspberrypi,pico-flash-controller"
level: 0
actions:
- type: instance
value:
raw:
type: myself
device: crate::device::flash::FlashController

# Flash partitions exist as children of a node compatible with "soc-nv-flash" that itself is a child
# of the controller itself.
# TODO: Get the write and erase property from the DT if present.
- name: flash-partition
rules:
- type: compatible
value:
names:
- "fixed-partitions"
level: 1
- type: compatible
value:
names:
- "soc-nv-flash"
level: 2
actions:
- type: instance
value:
raw:
type: parent
value:
level: 3
args:
- type: reg
device: "crate::device::flash::FlashPartition"

# Generate a pseudo node that matches all of the labels across the tree with their nodes.
- name: labels
rules:
- type: root
actions:
- type: labels

2 changes: 0 additions & 2 deletions etc/platforms.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
-p mps2/an385
-p mps2/an521/cpu0
-p qemu_cortex_m0
-p qemu_cortex_m3
-p qemu_riscv32
Expand Down
7 changes: 7 additions & 0 deletions samples/blinky/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: Apache-2.0 OR MIT

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(blinky)

rust_cargo_application()
20 changes: 20 additions & 0 deletions samples/blinky/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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 = "Blink an LED forever using the GPIO API"
license = "Apache-2.0 OR MIT"

[lib]
crate-type = ["staticlib"]

[dependencies]
zephyr = "0.1.0"
log = "0.4.22"

[build-dependencies]
zephyr-build = "0.1.0"
97 changes: 97 additions & 0 deletions samples/blinky/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
.. zephyr:code-sample:: blinky
:name: Blinky
:relevant-api: gpio_interface

Blink an LED forever using the GPIO API.

Overview
********

The Blinky sample blinks an LED forever using the :ref:`GPIO API <gpio_api>`.

The source code shows how to:

#. Get a pin specification from the :ref:`devicetree <dt-guide>` as a
:c:struct:`gpio_dt_spec`
#. Configure the GPIO pin as an output
#. Toggle the pin forever

See :zephyr:code-sample:`pwm-blinky` for a similar sample that uses the PWM API instead.

.. _blinky-sample-requirements:

Requirements
************

Your board must:

#. Have an LED connected via a GPIO pin (these are called "User LEDs" on many of
Zephyr's :ref:`boards`).
#. Have the LED configured using the ``led0`` devicetree alias.

Building and Running
********************

Build and flash Blinky as follows, changing ``reel_board`` for your board:

.. zephyr-app-commands::
:zephyr-app: samples/basic/blinky
:board: reel_board
:goals: build flash
:compact:

After flashing, the LED starts to blink and messages with the current LED state
are printed on the console. If a runtime error occurs, the sample exits without
printing to the console.

Build errors
************

You will see a build error at the source code line defining the ``struct
gpio_dt_spec led`` variable if you try to build Blinky for an unsupported
board.

On GCC-based toolchains, the error looks like this:

.. code-block:: none

error: '__device_dts_ord_DT_N_ALIAS_led_P_gpios_IDX_0_PH_ORD' undeclared here (not in a function)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the error rustc will spit out? I kinda doubt it...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the linker specifically. That's the lovely names in the generated C code that we reference in the get_instance.


Adding board support
********************

To add support for your board, add something like this to your devicetree:

.. code-block:: DTS

/ {
aliases {
led0 = &myled0;
};

leds {
compatible = "gpio-leds";
myled0: led_0 {
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
};
};
};
Comment on lines +67 to +78
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is mixed tab and spaces indentation here, I assume by accident

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that is rather weird, and I'm not sure what the right thing to do is. In Zephyr, DTS files are indented with tabs, and this is a snippet of that. I think this will cause the tabs to show up in the output. RTS is a little weird about it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

github's render looks to have correct spacing.


The above sets your board's ``led0`` alias to use pin 13 on GPIO controller
``gpio0``. The pin flags :c:macro:`GPIO_ACTIVE_HIGH` mean the LED is on when
the pin is set to its high state, and off when the pin is in its low state.

Tips:

- See :dtcompatible:`gpio-leds` for more information on defining GPIO-based LEDs
in devicetree.

- If you're not sure what to do, check the devicetrees for supported boards which
use the same SoC as your target. See :ref:`get-devicetree-outputs` for details.

- See :zephyr_file:`include/zephyr/dt-bindings/gpio/gpio.h` for the flags you can use
in devicetree.

- If the LED is built in to your board hardware, the alias should be defined in
your :ref:`BOARD.dts file <devicetree-in-out-files>`. Otherwise, you can
define one in a :ref:`devicetree overlay <set-devicetree-overlays>`.
9 changes: 9 additions & 0 deletions samples/blinky/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fn main() {
// This call will make make config entries available in the code for every device tree node, to
// allow conditional compilation based on whether it is present in the device tree.
// For example, it will be possible to have:
// ```rust
// #[cfg(dt = "aliases::led0")]
// ```
zephyr_build::dt_cfgs();
}
10 changes: 10 additions & 0 deletions samples/blinky/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CONFIG_GPIO=y

CONFIG_RUST=y
CONFIG_RUST_ALLOC=y

CONFIG_DEBUG=y
CONFIG_MAIN_STACK_SIZE=8192

# Verify that userspace builds work.
# CONFIG_USERSPACE=y
13 changes: 13 additions & 0 deletions samples/blinky/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# See doc/develop/test/twister.rst for what is here.
sample:
name: Blinky Sample
tests:
sample.basic.blinky:
tags:
- LED
- gpio
filter: dt_enabled_alias_with_parent_compat("led0", "gpio-leds")
depends_on: gpio
harness: led
integration_platforms:
- frdm_k64f
52 changes: 52 additions & 0 deletions samples/blinky/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2024 Linaro LTD
// SPDX-License-Identifier: Apache-2.0

#![no_std]

// Sigh. The check config system requires that the compiler be told what possible config values
// there might be. This is completely impossible with both Kconfig and the DT configs, since the
// whole point is that we likely need to check for configs that aren't otherwise present in the
// build. So, this is just always necessary.
#![allow(unexpected_cfgs)]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put the expected cfg and cfg values in [lints.*] in Cargo.toml:

https://blog.rust-lang.org/2024/05/06/check-cfg.html

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't possible, as we don't know the set of possible cfgs. This is actually kind of something broken about the way Rust is doing it. Other than scanning the entire Zephyr Kconfig tree for possible configs, we have no way of knowing what things the user's code might want to be able to config on.

Now, admittedly an app could add their own, and maybe we should do that? Is that what you are suggesting?


use log::warn;

use zephyr::raw::GPIO_OUTPUT_ACTIVE;
use zephyr::time::{ Duration, sleep };

#[no_mangle]
extern "C" fn rust_main() {
unsafe { zephyr::set_logger().unwrap(); }

warn!("Starting blinky");

do_blink();
}

#[cfg(dt = "aliases::led0")]
fn do_blink() {
warn!("Inside of blinky");

let mut led0 = zephyr::devicetree::aliases::led0::get_instance().unwrap();
let mut gpio_token = unsafe { zephyr::device::gpio::GpioToken::get_instance().unwrap() };

if !led0.is_ready() {
warn!("LED is not ready");
loop {
}
Comment on lines +35 to +36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
loop {
}
loop {}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should get fixed when I do a rustfmt cleanup.

}

unsafe { led0.configure(&mut gpio_token, GPIO_OUTPUT_ACTIVE); }
let duration = Duration::millis_at_least(500);
loop {
unsafe { led0.toggle_pin(&mut gpio_token); }
sleep(duration);
}
}

#[cfg(not(dt = "aliases::led0"))]
fn do_blink() {
warn!("No leds configured");
loop {
}
Comment on lines +50 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
loop {
}
loop {}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll let this get fixed when I do the big rustfmt/clippy fixes after this merges.

}
7 changes: 7 additions & 0 deletions zephyr-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ Provides utilities for accessing Kconfig and devicetree information.
# used by the core Zephyr tree, but are needed by zephyr applications.
[dependencies]
regex = "1.10.3"
pest = "2.6"
pest_derive = "2.6"
quote = "1.0"
proc-macro2 = "1.0.86"
serde = { version = "1.0", features = ["derive"] }
serde_yaml_ng = "0.10"
anyhow = "1.0.89"
Loading