From 7fa6c1fc7f84b9b74a0581ad0c427bd7eaa7284b Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 5 Apr 2024 12:55:50 -0700 Subject: [PATCH] stm32xx-sys: generate constants for EXTI pins 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 --- Cargo.lock | 1 + build/stm32xx-sys/src/lib.rs | 73 +++++++++++++++++++++++++++-- build/util/src/lib.rs | 9 ++-- drv/stm32xx-sys-api/Cargo.toml | 1 + drv/stm32xx-sys-api/build.rs | 1 + drv/stm32xx-sys-api/src/lib.rs | 1 + task/nucleo-user-button/src/main.rs | 7 +-- 7 files changed, 81 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8bfc35eb4a..a580ead378 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2038,6 +2038,7 @@ dependencies = [ name = "drv-stm32xx-sys-api" version = "0.1.0" dependencies = [ + "build-stm32xx-sys", "byteorder", "cfg-if", "counters", diff --git a/build/stm32xx-sys/src/lib.rs b/build/stm32xx-sys/src/lib.rs index b5498a091c..0018f734d5 100644 --- a/build/stm32xx-sys/src/lib.rs +++ b/build/stm32xx-sys/src/lib.rs @@ -2,14 +2,16 @@ // 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, } @@ -17,7 +19,7 @@ pub struct SysConfig { #[serde(rename_all = "kebab-case", deny_unknown_fields)] struct GpioIrqConfig { port: Port, - pin: u8, + pin: usize, owner: GpioIrqOwner, } @@ -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::("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::>>()?; + + // 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 { Ok(build_util::task_maybe_config::()?.unwrap_or_default()) @@ -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!( @@ -181,3 +239,10 @@ impl SysConfig { }) } } + +fn to_const_name(mut s: String) -> anyhow::Result { + s.make_ascii_uppercase(); + let s = s.replace("-", "_"); + syn::parse_str::(&s) + .with_context(|| format!("`{s}` is not a valid Rust identifier")) +} diff --git a/build/util/src/lib.rs b/build/util/src/lib.rs index c4f0626759..5054e47676 100644 --- a/build/util/src/lib.rs +++ b/build/util/src/lib.rs @@ -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!( @@ -104,12 +109,10 @@ pub fn config() -> Result { /// Pulls the task configuration. See `config` for more details. pub fn task_config() -> Result { - 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() ) }) } diff --git a/drv/stm32xx-sys-api/Cargo.toml b/drv/stm32xx-sys-api/Cargo.toml index d469746341..60fc89b848 100644 --- a/drv/stm32xx-sys-api/Cargo.toml +++ b/drv/stm32xx-sys-api/Cargo.toml @@ -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"] diff --git a/drv/stm32xx-sys-api/build.rs b/drv/stm32xx-sys-api/build.rs index 41c969ce97..4bd6cdd3f1 100644 --- a/drv/stm32xx-sys-api/build.rs +++ b/drv/stm32xx-sys-api/build.rs @@ -3,6 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. fn main() -> Result<(), Box> { + build_stm32xx_sys::build_gpio_irq_pins()?; idol::client::build_client_stub( "../../idl/stm32xx-sys.idol", "client_stub.rs", diff --git a/drv/stm32xx-sys-api/src/lib.rs b/drv/stm32xx-sys-api/src/lib.rs index 5931eb2301..f4fd5cc5a3 100644 --- a/drv/stm32xx-sys-api/src/lib.rs +++ b/drv/stm32xx-sys-api/src/lib.rs @@ -367,3 +367,4 @@ impl From> for IrqControl { } include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); +include!(concat!(env!("OUT_DIR"), "/gpio_irq_pins.rs")); diff --git a/task/nucleo-user-button/src/main.rs b/task/nucleo-user-button/src/main.rs index ae3acf667d..8f85c4c5fe 100644 --- a/task/nucleo-user-button/src/main.rs +++ b/task/nucleo-user-button/src/main.rs @@ -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::*; @@ -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, );