Skip to content

Commit

Permalink
stm32xx-sys: generate constants for EXTI pins
Browse files Browse the repository at this point in the history
Currently, pins used for EXTI IRQs are defined in the `sys` task's
config in `app.toml`. When a task wishes to actually use those pins,
though, it must _also_ define them separately in order to call
`sys.gpio_configure_input` (and if it wants to perform its own GPIO
reads on that pin). This is generally done either by hard-coding
the port and pin number in the task's source code, or by defining its
own config for configuring which pin to use.

This is unfortunate, as it results in a duplicated pin definition,
either between the source code and the config if hard-coded, or twice in
the config, if the task also gets the definition from config. And, if
the task configures the pin in its own config, the author of that task
must re-implement the config handling.

This commit fixes this by adding new code generation to
`drv-stm32xx-sys-api` to generate a module called `gpio_irq_pins`
containing `PinSet` constants with the name of the IRQ pin and the pin
number and port defined in the `sys` task's config section. This way,
the same pin set used by `sys` when configuring the EXTI interrupt can
be referenced by the task that consumes the interrupt, removing the need
to duplicate the pin definition. I've edited the `nucleo-user-button`
demo task to demonstrate using this.

Signed-off-by: Eliza Weisman <[email protected]>
  • Loading branch information
hawkw committed Apr 5, 2024
1 parent ee99ba7 commit 7fa6c1f
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 69 additions & 4 deletions build/stm32xx-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use anyhow::Context;
use quote::{quote, TokenStreamExt};
use serde::Deserialize;
use std::collections::BTreeMap;
use std::{collections::BTreeMap, io::Write};

#[derive(Deserialize, Default)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[serde(rename_all = "kebab-case")]
pub struct SysConfig {
/// EXTI interrupts
#[serde(default)]
gpio_irqs: BTreeMap<String, GpioIrqConfig>,
}

#[derive(Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
struct GpioIrqConfig {
port: Port,
pin: u8,
pin: usize,
owner: GpioIrqOwner,
}

Expand Down Expand Up @@ -68,6 +70,62 @@ to_tokens_enum! {
}
}

pub fn build_gpio_irq_pins() -> anyhow::Result<()> {
let out_dir = build_util::out_dir();
let dest_path = out_dir.join("gpio_irq_pins.rs");
let mut out = std::fs::File::create(&dest_path).with_context(|| {
format!("failed to create file '{}'", dest_path.display())
})?;

let Some(sys_config) =
build_util::other_task_full_config::<SysConfig>("sys")?.config
else {
// No GPIO IRQs are configured; nothing left to do here!
return Ok(());
};

let task = build_util::task_name();
let pins = sys_config
.gpio_irqs
.iter()
.filter_map(|(name, cfg)| {
let &GpioIrqConfig {
pin,
port,
ref owner,
} = cfg;
// Only generate constants for pins owned by the current task.
if owner.name != task {
return None;
}

let name = match to_const_name(name.clone()) {
Ok(name) => name,
Err(e) => return Some(Err(e)),
};

Some(Ok(quote! {
pub const #name: PinSet = #port.pin(#pin);
}))
})
.collect::<anyhow::Result<Vec<_>>>()?;

// Don't generate an empty module if there are no pins.
if pins.is_empty() {
return Ok(());
}

let tokens = quote! {
pub mod gpio_irq_pins {
use drv_stm32xx_gpio_common::{PinSet, Port};
#( #pins )*
}
};
writeln!(out, "{tokens}")?;

Ok(())
}

impl SysConfig {
pub fn load() -> anyhow::Result<Self> {
Ok(build_util::task_maybe_config::<Self>()?.unwrap_or_default())
Expand Down Expand Up @@ -111,7 +169,7 @@ impl SysConfig {
let task = syn::parse_str(&owner.name)?;
let note = quote::format_ident!(
"{}_MASK",
owner.notification.to_uppercase().replace('-', "_")
to_const_name(owner.notification.clone())?
);

let name = quote::format_ident!(
Expand Down Expand Up @@ -181,3 +239,10 @@ impl SysConfig {
})
}
}

fn to_const_name(mut s: String) -> anyhow::Result<syn::Ident> {
s.make_ascii_uppercase();
let s = s.replace("-", "_");
syn::parse_str::<syn::Ident>(&s)
.with_context(|| format!("`{s}` is not a valid Rust identifier"))
}
9 changes: 6 additions & 3 deletions build/util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ pub fn target_os() -> String {
std::env::var("CARGO_CFG_TARGET_OS").unwrap()
}

/// Reads the `HUBRIS_TASK_NAME` env var.
pub fn task_name() -> String {
crate::env_var("HUBRIS_TASK_NAME").expect("missing HUBRIS_TASK_NAME")
}

/// Checks to see whether the given feature is enabled
pub fn has_feature(s: &str) -> bool {
std::env::var(format!(
Expand Down Expand Up @@ -104,12 +109,10 @@ pub fn config<T: DeserializeOwned>() -> Result<T> {

/// Pulls the task configuration. See `config` for more details.
pub fn task_config<T: DeserializeOwned>() -> Result<T> {
let task_name =
crate::env_var("HUBRIS_TASK_NAME").expect("missing HUBRIS_TASK_NAME");
task_maybe_config()?.ok_or_else(|| {
anyhow!(
"app.toml missing task config section [tasks.{}.config]",
task_name
task_name()
)
})
}
Expand Down
1 change: 1 addition & 0 deletions drv/stm32xx-sys-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ userlib = { path = "../../sys/userlib" }

[build-dependencies]
idol.workspace = true
build-stm32xx-sys = { path = "../../build/stm32xx-sys" }

[features]
family-stm32h7 = ["drv-stm32xx-gpio-common/family-stm32h7"]
Expand Down
1 change: 1 addition & 0 deletions drv/stm32xx-sys-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
build_stm32xx_sys::build_gpio_irq_pins()?;
idol::client::build_client_stub(
"../../idl/stm32xx-sys.idol",
"client_stub.rs",
Expand Down
1 change: 1 addition & 0 deletions drv/stm32xx-sys-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,4 @@ impl From<Option<bool>> for IrqControl {
}

include!(concat!(env!("OUT_DIR"), "/client_stub.rs"));
include!(concat!(env!("OUT_DIR"), "/gpio_irq_pins.rs"));
7 changes: 2 additions & 5 deletions task/nucleo-user-button/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#![no_std]
#![no_main]

use drv_stm32xx_sys_api::{Edge, IrqControl, PinSet, Port, Pull};
use drv_stm32xx_sys_api::{Edge, IrqControl, Pull};
use ringbuf::ringbuf_entry;
use userlib::*;

Expand Down Expand Up @@ -72,10 +72,7 @@ pub fn main() -> ! {
});

sys.gpio_configure_input(
PinSet {
port: Port::C,
pin_mask: (1 << 13),
},
drv_stm32xx_sys_api::gpio_irq_pins::BUTTON,
Pull::None,
);

Expand Down

0 comments on commit 7fa6c1f

Please sign in to comment.