diff --git a/Cargo.lock b/Cargo.lock index 8985f680f..afb319ded 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1498,6 +1498,48 @@ dependencies = [ "userlib", ] +[[package]] +name = "drv-stm32h7-exti" +version = "0.1.0" +dependencies = [ + "drv-stm32h7-exti-api", + "drv-stm32xx-gpio-common", + "num-traits", + "ringbuf", + "stm32h7", + "userlib", + "zerocopy", +] + +[[package]] +name = "drv-stm32h7-exti-api" +version = "0.1.0" +dependencies = [ + "bitflags", + "derive-idol-err", + "drv-stm32xx-gpio-common", + "idol", + "num-traits", + "userlib", + "zerocopy", +] + +[[package]] +name = "drv-stm32h7-exti-server" +version = "0.1.0" +dependencies = [ + "drv-stm32h7-exti", + "drv-stm32h7-exti-api", + "drv-stm32xx-gpio-common", + "idol", + "idol-runtime", + "num-traits", + "ringbuf", + "stm32h7", + "userlib", + "zerocopy", +] + [[package]] name = "drv-stm32h7-hash" version = "0.1.0" diff --git a/app/demo-stm32h7-nucleo/app-h743.toml b/app/demo-stm32h7-nucleo/app-h743.toml index c4d81bbf7..9d06481c4 100644 --- a/app/demo-stm32h7-nucleo/app-h743.toml +++ b/app/demo-stm32h7-nucleo/app-h743.toml @@ -30,6 +30,16 @@ uses = ["rcc", "gpios1", "gpios2", "gpios3"] start = true task-slots = ["jefe"] +[tasks.exti_driver] +name = "drv-stm32h7-exti-server" +features = ["h743"] +priority = 2 +max-sizes = {flash = 16384, ram = 4096} +uses = ["exti", "syscfg"] +start = true +interrupts = {"exti.exti0" = 0b1, "exti.exti1" = 0b10, "exti.exti2" = 0b100, "exti.exti3" = 0b1000, "exti.exti4" = 0b1_0000, "exti.exti9_5" = 0b10_0000, "exti.exti15_10" = 0b100_0000} +task-slots = ["sys"] + [tasks.i2c_driver] name = "drv-stm32xx-i2c-server" features = ["h743"] diff --git a/app/demo-stm32h7-nucleo/app-h753.toml b/app/demo-stm32h7-nucleo/app-h753.toml index 75f5289ca..4d1b03114 100644 --- a/app/demo-stm32h7-nucleo/app-h753.toml +++ b/app/demo-stm32h7-nucleo/app-h753.toml @@ -30,6 +30,16 @@ uses = ["rcc", "gpios1", "gpios2", "gpios3"] start = true task-slots = ["jefe"] +[tasks.exti_driver] +name = "drv-stm32h7-exti-server" +features = ["h743"] +priority = 2 +max-sizes = {flash = 16384, ram = 4096} +uses = ["exti", "syscfg"] +start = true +interrupts = {"exti.exti0" = 0b1, "exti.exti1" = 0b10, "exti.exti2" = 0b100, "exti.exti3" = 0b1000, "exti.exti4" = 0b1_0000, "exti.exti9_5" = 0b10_0000, "exti.exti15_10" = 0b100_0000} +task-slots = ["sys"] + [tasks.i2c_driver] name = "drv-stm32xx-i2c-server" features = ["h753"] diff --git a/chips/stm32h7/chip.toml b/chips/stm32h7/chip.toml index ddb42a4dd..49a4c4403 100644 --- a/chips/stm32h7/chip.toml +++ b/chips/stm32h7/chip.toml @@ -161,3 +161,11 @@ size = 0x8000 address = 0x38000000 size = 0x10000 +[exti] +address = 0x58000000 +size = 1024 +interrupts = { exti0 = 6, exti1 = 7, exti2 = 8, exti3 = 9, exti4 = 10, exti9_5 = 23, exti15_10 = 40 } + +[syscfg] +address = 0x58000400 +size = 1024 diff --git a/drv/stm32h7-exti-api/Cargo.toml b/drv/stm32h7-exti-api/Cargo.toml new file mode 100644 index 000000000..cd29372a1 --- /dev/null +++ b/drv/stm32h7-exti-api/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "drv-stm32h7-exti-api" +version = "0.1.0" +edition = "2021" + +[dependencies] +bitflags = {workspace = true} +num-traits = { workspace = true } +zerocopy = { workspace = true } + +derive-idol-err = {path = "../../lib/derive-idol-err" } +drv-stm32xx-gpio-common = {path = "../stm32xx-gpio-common"} +userlib = {path = "../../sys/userlib"} + +[features] +family-stm32h7 = ["drv-stm32xx-gpio-common/family-stm32h7"] +h743 = ["drv-stm32xx-gpio-common/model-stm32h743"] +#h747cm7 = ["drv-stm32xx-gpio-common/model-stm32h747cm7"] +h753 = ["drv-stm32xx-gpio-common/model-stm32h753"] + +# This section is here to discourage RLS/rust-analyzer from doing test builds, +# since test builds don't work for cross compilation. +[lib] +test = false +bench = false + +[build-dependencies] +idol = { workspace = true } diff --git a/drv/stm32h7-exti-api/build.rs b/drv/stm32h7-exti-api/build.rs new file mode 100644 index 000000000..eacb8ce1d --- /dev/null +++ b/drv/stm32h7-exti-api/build.rs @@ -0,0 +1,8 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// 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/. + +fn main() -> Result<(), Box> { + idol::client::build_client_stub("../../idl/stm32h7-exti.idol", "client_stub.rs")?; + Ok(()) +} diff --git a/drv/stm32h7-exti-api/src/lib.rs b/drv/stm32h7-exti-api/src/lib.rs new file mode 100644 index 000000000..bea65f45e --- /dev/null +++ b/drv/stm32h7-exti-api/src/lib.rs @@ -0,0 +1,41 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// 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/. + +//! Client API for the EXTI server + +#![no_std] + +use bitflags::bitflags; +use derive_idol_err::IdolError; +use drv_stm32xx_gpio_common::Port; +use userlib::*; + +#[derive(Copy, Clone, Debug, FromPrimitive, Eq, PartialEq, IdolError)] +#[repr(u32)] +pub enum ExtiError { + AlreadyRegistered = 1, + InvalidIndex, + NotOwner, + NotRegistered, +} + +bitflags! { + + pub struct Edge: u8 { + const RISING = 0b01; + const FALLING = 0b10; + const RISING_AND_FALLING = Self::RISING.bits | Self::FALLING.bits; + } + +} + +impl Exti { + + pub fn enable_gpio(&self, port: Port, index: usize, edges: Edge, notification: u32) -> Result<(), ExtiError> { + self.enable_gpio_raw(port, index, edges.bits, notification) + } + +} + +include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); diff --git a/drv/stm32h7-exti-server/Cargo.toml b/drv/stm32h7-exti-server/Cargo.toml new file mode 100644 index 000000000..e68b7ca6a --- /dev/null +++ b/drv/stm32h7-exti-server/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "drv-stm32h7-exti-server" +version = "0.1.0" +edition = "2021" + +[dependencies] +idol-runtime = { workspace = true } +num-traits = { workspace = true } +stm32h7 = { workspace = true } +zerocopy = { workspace = true } + +drv-stm32h7-exti = { path = "../stm32h7-exti" } +drv-stm32h7-exti-api = { path = "../stm32h7-exti-api" } +drv-stm32xx-gpio-common = {path = "../stm32xx-gpio-common"} +ringbuf = { path = "../../lib/ringbuf" } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } + +[build-dependencies] +idol = { workspace = true } + +[features] +family-stm32h7 = ["drv-stm32xx-gpio-common/family-stm32h7"] +h743 = ["stm32h7/stm32h743", "drv-stm32h7-exti/h743", "drv-stm32xx-gpio-common/model-stm32h743"] +#h747cm7 = ["stm32h7/stm32h747cm7", "drv-stm32h7-exti/h747cm7", "drv-stm32xx-gpio-common/model-stm32h747cm7"] +h753 = ["stm32h7/stm32h753", "drv-stm32h7-exti/h753", "drv-stm32xx-gpio-common/model-stm32h753"] + +# This section is here to discourage RLS/rust-analyzer from doing test builds, +# since test builds don't work for cross compilation. +[[bin]] +name = "drv-stm32h7-exti-server" +test = false +bench = false diff --git a/drv/stm32h7-exti-server/build.rs b/drv/stm32h7-exti-server/build.rs new file mode 100644 index 000000000..58c761a06 --- /dev/null +++ b/drv/stm32h7-exti-server/build.rs @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// 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/. + +fn main() -> Result<(), Box> { + idol::server::build_server_support( + "../../idl/stm32h7-exti.idol", + "server_stub.rs", + idol::server::ServerStyle::InOrder, + )?; + Ok(()) +} diff --git a/drv/stm32h7-exti-server/src/main.rs b/drv/stm32h7-exti-server/src/main.rs new file mode 100644 index 000000000..6e8326e2f --- /dev/null +++ b/drv/stm32h7-exti-server/src/main.rs @@ -0,0 +1,197 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// 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/. + +#![no_std] +#![no_main] + +use drv_stm32h7_exti_api::{Edge, ExtiError}; +use drv_stm32h7_exti::Exti; +use drv_stm32xx_gpio_common::Port; +use idol_runtime::RequestError; + +use ringbuf::*; + +#[cfg(feature = "h743")] +use stm32h7::stm32h743 as device; + +#[cfg(feature = "h747cm7")] +use stm32h7::stm32h747cm7 as device; + +#[cfg(feature = "h753")] +use stm32h7::stm32h753 as device; + +use userlib::*; + +#[derive(Copy, Clone, PartialEq)] +enum Trace { + Irq { bits: u32 }, + Register { port: Port, index: usize, edges: u8, notification: u32 }, + Unregister { index: usize }, + None, +} + +ringbuf!(Trace, 64, Trace::None); + +const IRQ0_MASK: u32 = 0b0000_0001; +const IRQ1_MASK: u32 = 0b0000_0010; +const IRQ2_MASK: u32 = 0b0000_0100; +const IRQ3_MASK: u32 = 0b0000_1000; +const IRQ4_MASK: u32 = 0b0001_0000; +const IRQ9_5_MASK: u32 = 0b0010_0000; +const IRQ15_10_MASK: u32 = 0b0100_0000; + +#[export_name = "main"] +fn main() -> ! { + let mut server = ServerImpl { + exti: Exti::new(), + irq_mask: 0, + notification_mask: 0, + }; + let mut incoming = [0u8; INCOMING_SIZE]; + loop { + idol_runtime::dispatch_n(&mut incoming, &mut server); + } +} + +struct ServerImpl { + exti: Exti, + irq_mask: u16, + notification_mask: u32, +} + +impl ServerImpl { + + fn index_to_mask(index: usize) -> Option { + match index { + 0 => Some(IRQ0_MASK), + 1 => Some(IRQ1_MASK), + 2 => Some(IRQ2_MASK), + 3 => Some(IRQ3_MASK), + 4 => Some(IRQ4_MASK), + 5 | 6 | 7 | 8 | 9 => Some(IRQ9_5_MASK), + 10 | 11 | 12 | 13 | 14 | 15 => Some(IRQ15_10_MASK), + _ => None, + } + } +} + +impl InOrderExtiImpl for ServerImpl { + + fn enable_gpio_raw( + &mut self, + msg: &RecvMessage, + port: Port, + index: usize, + edges: u8, + notification: u32, + ) -> Result<(), RequestError> { + ringbuf_entry!(Trace::Register { port, index, edges, notification }); + + let mask = Self::index_to_mask(index) + .ok_or(ExtiError::InvalidIndex)?; + + self.exti.enable_gpio( + port, index, + Edge::from_bits_truncate(edges), + msg.sender, notification + )?; + + self.irq_mask |= 1 << index; + + sys_irq_control(mask, true); + self.notification_mask |= mask; + + Ok(()) + } + + fn disable_gpio( + &mut self, + msg: &RecvMessage, + index: usize, + ) -> Result<(), RequestError> { + ringbuf_entry!(Trace::Unregister { index }); + + let mask = Self::index_to_mask(index) + .ok_or(ExtiError::InvalidIndex)?; + + self.exti.disable_gpio(index, msg.sender)?; + + self.irq_mask &= !(1 << index); + + match mask { + IRQ9_5_MASK => if self.irq_mask & 0b0000_0011_1110_0000 == 0 { + sys_irq_control(mask, false); + self.notification_mask &= !mask; + }, + IRQ15_10_MASK => if self.irq_mask & 0b1111_1100_0000_0000 == 0 { + sys_irq_control(mask, false); + self.notification_mask &= !mask; + }, + _ => { + sys_irq_control(mask, false); + self.notification_mask &= !mask; + } + } + Ok(()) + } + +} + +macro_rules! handle_irq { + ($exti:expr, $pr1:ident, $pr:ident, $idx:literal, $bits:ident, $mask:ident) => { + if $bits & $mask != 0 { + $pr1.modify(|_, w| w.$pr().set_bit()); + $exti.notify($idx); + sys_irq_control($mask, true); + } + }; + ($exti:expr, $pr1:ident, $pr:ident, $idx:literal) => { + if $pr1.read().$pr().bit_is_set() { + $pr1.modify(|_, w| w.$pr().set_bit()); + $exti.notify($idx); + } + }; +} + +impl idol_runtime::NotificationHandler for ServerImpl { + + fn current_notification_mask(&self) -> u32 { + self.notification_mask + } + + fn handle_notification(&mut self, bits: u32) { + ringbuf_entry!(Trace::Irq { bits }); + + #[cfg(any(feature = "h743", feature = "h753"))] + let pr1 = &unsafe { &*device::EXTI::ptr() }.cpupr1; + #[cfg(feature = "h747cm7")] + let pr1 = &unsafe { &*device::EXTI::ptr() }.c1pr1; + + handle_irq!(self.exti, pr1, pr0, 0, bits, IRQ0_MASK); + handle_irq!(self.exti, pr1, pr1, 1, bits, IRQ1_MASK); + handle_irq!(self.exti, pr1, pr2, 2, bits, IRQ2_MASK); + handle_irq!(self.exti, pr1, pr3, 3, bits, IRQ3_MASK); + handle_irq!(self.exti, pr1, pr4, 4, bits, IRQ4_MASK); + if bits & IRQ9_5_MASK != 0 { + handle_irq!(self.exti, pr1, pr5, 5); + handle_irq!(self.exti, pr1, pr6, 6); + handle_irq!(self.exti, pr1, pr7, 7); + handle_irq!(self.exti, pr1, pr8, 8); + handle_irq!(self.exti, pr1, pr9, 9); + sys_irq_control(IRQ9_5_MASK, true); + } + if bits & IRQ15_10_MASK != 0 { + handle_irq!(self.exti, pr1, pr10, 10); + handle_irq!(self.exti, pr1, pr11, 11); + handle_irq!(self.exti, pr1, pr12, 12); + handle_irq!(self.exti, pr1, pr13, 13); + handle_irq!(self.exti, pr1, pr14, 14); + handle_irq!(self.exti, pr1, pr15, 15); + sys_irq_control(IRQ15_10_MASK, true); + } + } + +} + +include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); diff --git a/drv/stm32h7-exti/Cargo.toml b/drv/stm32h7-exti/Cargo.toml new file mode 100644 index 000000000..1cce5c901 --- /dev/null +++ b/drv/stm32h7-exti/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "drv-stm32h7-exti" +version = "0.1.0" +edition = "2021" + +[dependencies] +num-traits = { workspace = true } +stm32h7 = { workspace = true } +zerocopy = { workspace = true } + +drv-stm32h7-exti-api = {path = "../stm32h7-exti-api"} +drv-stm32xx-gpio-common = {path = "../stm32xx-gpio-common"} +ringbuf = { path = "../../lib/ringbuf" } +userlib = {path = "../../sys/userlib"} + +[features] +family-stm32h7 = ["drv-stm32xx-gpio-common/family-stm32h7"] +h743 = ["stm32h7/stm32h743", "drv-stm32xx-gpio-common/model-stm32h743"] +#h747cm7 = ["stm32h7/stm32h747cm7", "drv-stm32xx-gpio-common/model-stm32h747cm7"] +h753 = ["stm32h7/stm32h753", "drv-stm32xx-gpio-common/model-stm32h753"] diff --git a/drv/stm32h7-exti/src/lib.rs b/drv/stm32h7-exti/src/lib.rs new file mode 100644 index 000000000..a53f59f13 --- /dev/null +++ b/drv/stm32h7-exti/src/lib.rs @@ -0,0 +1,174 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// 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/. + +#![no_std] + +use drv_stm32xx_gpio_common::Port; +use drv_stm32h7_exti_api::{Edge, ExtiError}; +use userlib::*; + +#[cfg(feature = "h743")] +use stm32h7::stm32h743 as device; + +#[cfg(feature = "h747cm7")] +use stm32h7::stm32h747cm7 as device; + +#[cfg(feature = "h753")] +use stm32h7::stm32h753 as device; + +#[derive(Debug, Copy, Clone)] +struct Entry { + task: TaskId, + notification: u32, +} + +const ENTRY_COUNT: usize = 16; + +pub struct Exti { + syscfg: &'static device::syscfg::RegisterBlock, + exti: &'static device::exti::RegisterBlock, + entries: [Option; ENTRY_COUNT], +} + +macro_rules! enable_pin { + ($self:ident, $port:ident, $edge:ident, $exticr:ident, $exti:ident, $tr:ident, $mr:ident) => { + { + $self.syscfg.$exticr.modify(|_, w| + unsafe { w.$exti().bits($port as u8) } + ); + $self.exti.ftsr1.modify(|_, w| + if $edge.contains(Edge::FALLING) { + w.$tr().set_bit() + } else { + w.$tr().clear_bit() + } + ); + $self.exti.rtsr1.modify(|_, w| + if $edge.contains(Edge::RISING) { + w.$tr().set_bit() + } else { + w.$tr().clear_bit() + } + ); + #[cfg(any(feature = "h743", feature = "h753"))] + { + $self.exti.cpuimr1.modify(|_, w| w.$mr().set_bit()); + $self.exti.cpuemr1.modify(|_, w| w.$mr().clear_bit()); + } + #[cfg(feature = "h747cm7")] + { + $self.exti.c1imr1.modify(|_, w| w.$mr().set_bit()); + $self.exti.c1emr1.modify(|_, w| w.$mr().clear_bit()); + } + } + }; +} + +macro_rules! disable_pin { + ($self:ident, $mr:ident) => { + { + #[cfg(any(feature = "h743", feature = "h753"))] + $self.exti.cpuimr1.modify(|_, w| w.$mr().clear_bit()); + #[cfg(feature = "h747cm7")] + $self.exti.c1imr1.modify(|_, w| w.$mr().clear_bit()); + } + } +} + +impl Exti { + + pub fn new() -> Self { + Self { + syscfg: unsafe { &*device::SYSCFG::ptr() }, + exti: unsafe { &*device::EXTI::ptr() }, + entries: [None; ENTRY_COUNT], + } + } + + pub fn notify(&self, index: usize) { + if let Some(entry) = self.entries[index] { + sys_post(sys_refresh_task_id(entry.task), entry.notification); + } + } + + pub fn enable_gpio( + &mut self, + port: Port, + index: usize, + edges: Edge, + task: TaskId, + notification: u32, + ) -> Result<(), ExtiError> { + if index >= self.entries.len() { + return Err(ExtiError::InvalidIndex) + } + + if let Some(existing) = self.entries[index] { + if existing.task.index() != task.index() || existing.notification != notification { + return Err(ExtiError::AlreadyRegistered) + } + } + + self.entries[index] = Some(Entry { task, notification }); + + match index { + 0 => enable_pin!(self, port, edges, exticr1, exti0, tr0, mr0), + 1 => enable_pin!(self, port, edges, exticr1, exti1, tr1, mr1), + 2 => enable_pin!(self, port, edges, exticr1, exti2, tr2, mr2), + 3 => enable_pin!(self, port, edges, exticr1, exti3, tr3, mr3), + 4 => enable_pin!(self, port, edges, exticr2, exti4, tr4, mr4), + 5 => enable_pin!(self, port, edges, exticr2, exti5, tr5, mr5), + 6 => enable_pin!(self, port, edges, exticr2, exti6, tr6, mr6), + 7 => enable_pin!(self, port, edges, exticr2, exti7, tr7, mr7), + 8 => enable_pin!(self, port, edges, exticr3, exti8, tr8, mr8), + 9 => enable_pin!(self, port, edges, exticr3, exti9, tr9, mr9), + 10 => enable_pin!(self, port, edges, exticr3, exti10, tr10, mr10), + 11 => enable_pin!(self, port, edges, exticr3, exti11, tr11, mr11), + 12 => enable_pin!(self, port, edges, exticr4, exti12, tr12, mr12), + 13 => enable_pin!(self, port, edges, exticr4, exti13, tr13, mr13), + 14 => enable_pin!(self, port, edges, exticr4, exti14, tr14, mr14), + 15 => enable_pin!(self, port, edges, exticr4, exti15, tr15, mr15), + _ => panic!(), + } + + Ok(()) + } + + pub fn disable_gpio(&mut self, index: usize, task: TaskId) -> Result<(), ExtiError> { + if index >= self.entries.len() { + return Err(ExtiError::InvalidIndex) + } + + if let Some(existing) = self.entries[index] { + if existing.task.index() != task.index() { + return Err(ExtiError::NotOwner) + } + } else { + return Err(ExtiError::NotRegistered) + } + + match index { + 0 => disable_pin!(self, mr0), + 1 => disable_pin!(self, mr1), + 2 => disable_pin!(self, mr2), + 3 => disable_pin!(self, mr3), + 4 => disable_pin!(self, mr4), + 5 => disable_pin!(self, mr5), + 6 => disable_pin!(self, mr6), + 7 => disable_pin!(self, mr7), + 8 => disable_pin!(self, mr8), + 9 => disable_pin!(self, mr9), + 10 => disable_pin!(self, mr10), + 11 => disable_pin!(self, mr11), + 12 => disable_pin!(self, mr12), + 13 => disable_pin!(self, mr13), + 14 => disable_pin!(self, mr14), + 15 => disable_pin!(self, mr15), + _ => panic!(), + } + + Ok(()) + } + +} diff --git a/idl/stm32h7-exti.idol b/idl/stm32h7-exti.idol new file mode 100644 index 000000000..6208ccf9b --- /dev/null +++ b/idl/stm32h7-exti.idol @@ -0,0 +1,33 @@ +// STM32H7 EXTI IPC API + +Interface( + name: "Exti", + ops: { + "enable_gpio_raw": ( + args: { + "port": ( + type: "Port", + recv: FromPrimitive("u8"), + ), + "index": "usize", + "edges": "u8", + "notification": "u32", + }, + reply: Result( + ok: "()", + err: CLike("ExtiError"), + ), + idempotent: true, + ), + "disable_gpio": ( + args: { + "index": "usize", + }, + reply: Result( + ok: "()", + err: CLike("ExtiError"), + ), + idempotent: true, + ), + }, +)