Skip to content

Commit

Permalink
stm32xx-sys: generate constants for EXTI pins (#1734)
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 <eliza@elizas.website>
  • Loading branch information
hawkw authored Apr 5, 2024
1 parent 4a0ebad commit cb14452
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 24 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
17 changes: 5 additions & 12 deletions drv/stm32h7-sprot-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ const MAX_UPDATE_ATTEMPTS: u16 = 3;
// On the flipside, we have learned via unintended experiment that 5ms is too short!
const DUMP_TIMEOUT: u32 = 1000;

// ROT_IRQ comes from app.toml
// On Gemini, the STM32H753 is in a LQFP176 package with ROT_IRQ
// on pin2/PE3
use drv_stm32xx_sys_api::gpio_irq_pins::ROT_IRQ;

// We use spi3 on gimletlet and spi4 on gemini and gimlet.
// You should be able to move the RoT board between SPI3, SPI4, and SPI6
// without much trouble even though SPI3 is the preferred connector and
Expand All @@ -117,20 +120,10 @@ cfg_if::cfg_if! {
target_board = "psc-c",
target_board = "gemini-bu-1"
))] {
const ROT_IRQ: sys_api::PinSet = sys_api::PinSet {
// On Gemini, the STM32H753 is in a LQFP176 package with ROT_IRQ
// on pin2/PE3
port: sys_api::Port::E,
pin_mask: 1 << 3,
};
const ROT_SPI_DEVICE: u8 = drv_spi_api::devices::ROT;
fn debug_config(_sys: &sys_api::Sys) { }
fn debug_set(_sys: &sys_api::Sys, _asserted: bool) { }
} else if #[cfg(target_board = "gimletlet-2")] {
const ROT_IRQ: sys_api::PinSet = sys_api::PinSet {
port: sys_api::Port::D,
pin_mask: 1 << 0,
};
const DEBUG_PIN: sys_api::PinSet = sys_api::PinSet {
port: sys_api::Port::E,
pin_mask: 1 << 6,
Expand All @@ -151,7 +144,7 @@ cfg_if::cfg_if! {
}
const ROT_SPI_DEVICE: u8 = drv_spi_api::devices::SPI3_HEADER;
} else {
compile_error!("No configuration for ROT_IRQ");
compile_error!("No configuration for ROT_SPI_DEVICE");
}
}

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 cb14452

Please sign in to comment.