From 28747c80faa41badda701b9978aa319ce8bc02c5 Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Mon, 18 Nov 2024 10:05:05 -0800 Subject: [PATCH 1/4] Enable GPIO Pin Interrupts Add interrupt-related API calls to the LPC55 `gpio_driver`. A task on an LPC55 can now configue and use GPIO interrupts. app.toml example shows Pin Interrupt configuration: [tasks.foo] ... interrupts = { "pint.irq0" = "button-irq" } ... task-slots = ["gpio_driver", ...] [tasks.foo.config] pins = [ { name="BUTTON', pin={ port=1, pin=9}, alt=0, pint=0, direction="input", opendrain="normal" } ] --- Cargo.lock | 1 + app/lpc55xpresso/app-sprot.toml | 2 +- app/lpc55xpresso/app.toml | 2 +- app/oxide-rot-1/app-dev.toml | 2 +- app/oxide-rot-1/app.toml | 2 +- app/rot-carrier/app.toml | 4 +- build/lpc55pins/Cargo.toml | 1 + build/lpc55pins/src/lib.rs | 104 ++++++++++++++++-- chips/lpc55/chip.toml | 9 ++ drv/lpc55-gpio-api/src/lib.rs | 17 ++- drv/lpc55-gpio/src/main.rs | 171 +++++++++++++++++++++++++++-- drv/lpc55-i2c/src/main.rs | 2 + drv/lpc55-sprot-server/src/main.rs | 4 +- drv/user-leds/src/main.rs | 2 + idl/lpc55-pins.idol | 74 +++++++++++++ task/hiffy/src/lpc55.rs | 11 +- 16 files changed, 382 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e164766b5..88b08c0f2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -339,6 +339,7 @@ version = "0.1.0" dependencies = [ "anyhow", "build-util", + "call_rustfmt", "cfg-if", "convert_case", "indexmap 1.9.1", diff --git a/app/lpc55xpresso/app-sprot.toml b/app/lpc55xpresso/app-sprot.toml index 2fe6d99fbd..bb6394f1bd 100644 --- a/app/lpc55xpresso/app-sprot.toml +++ b/app/lpc55xpresso/app-sprot.toml @@ -76,7 +76,7 @@ task-slots = ["jefe"] name = "drv-lpc55-gpio" priority = 3 max-sizes = {flash = 8192, ram = 2048} -uses = ["gpio", "iocon"] +uses = ["gpio", "iocon", "pint", "inputmux"] start = true stacksize = 1000 task-slots = ["syscon_driver"] diff --git a/app/lpc55xpresso/app.toml b/app/lpc55xpresso/app.toml index 34bd7efec3..57df688918 100644 --- a/app/lpc55xpresso/app.toml +++ b/app/lpc55xpresso/app.toml @@ -70,7 +70,7 @@ task-slots = ["jefe"] name = "drv-lpc55-gpio" priority = 3 max-sizes = {flash = 8192, ram = 2048} -uses = ["gpio", "iocon"] +uses = ["gpio", "iocon", "pint", "inputmux"] start = true stacksize = 1000 task-slots = ["syscon_driver"] diff --git a/app/oxide-rot-1/app-dev.toml b/app/oxide-rot-1/app-dev.toml index c1b7fa6436..c5b33c1b7f 100644 --- a/app/oxide-rot-1/app-dev.toml +++ b/app/oxide-rot-1/app-dev.toml @@ -75,7 +75,7 @@ task-slots = ["jefe"] name = "drv-lpc55-gpio" priority = 3 max-sizes = {flash = 8192, ram = 2048} -uses = ["gpio", "iocon"] +uses = ["gpio", "iocon", "pint", "inputmux"] start = true task-slots = ["syscon_driver"] diff --git a/app/oxide-rot-1/app.toml b/app/oxide-rot-1/app.toml index 41a05d5646..c100267a24 100644 --- a/app/oxide-rot-1/app.toml +++ b/app/oxide-rot-1/app.toml @@ -61,7 +61,7 @@ task-slots = ["jefe"] name = "drv-lpc55-gpio" priority = 3 max-sizes = {flash = 8192, ram = 2048} -uses = ["gpio", "iocon"] +uses = ["gpio", "iocon", "pint", "inputmux"] start = true task-slots = ["syscon_driver"] diff --git a/app/rot-carrier/app.toml b/app/rot-carrier/app.toml index f1182218da..09e71bba97 100644 --- a/app/rot-carrier/app.toml +++ b/app/rot-carrier/app.toml @@ -67,7 +67,7 @@ task-slots = ["jefe"] name = "drv-lpc55-gpio" priority = 3 max-sizes = {flash = 8192, ram = 2048} -uses = ["gpio", "iocon"] +uses = ["gpio", "iocon", "pint", "inputmux"] start = true task-slots = ["syscon_driver"] @@ -75,7 +75,7 @@ task-slots = ["syscon_driver"] name = "drv-user-leds" features = ["lpc55"] priority = 6 -max-sizes = {flash = 8192, ram = 1056} +max-sizes = {flash = 8192, ram = 1120} start = true task-slots = ["gpio_driver"] notifications = ["timer"] diff --git a/build/lpc55pins/Cargo.toml b/build/lpc55pins/Cargo.toml index 2f6f8f0ec7..fa0c88c2c4 100644 --- a/build/lpc55pins/Cargo.toml +++ b/build/lpc55pins/Cargo.toml @@ -15,6 +15,7 @@ serde = { workspace = true } syn = { workspace = true } build-util = { path = "../util" } +call_rustfmt = { path = "../call_rustfmt"} [lints] workspace = true diff --git a/build/lpc55pins/src/lib.rs b/build/lpc55pins/src/lib.rs index ec63924e2e..c86cef0d5a 100644 --- a/build/lpc55pins/src/lib.rs +++ b/build/lpc55pins/src/lib.rs @@ -54,6 +54,7 @@ pub struct PinConfig { direction: Option, value: Option, name: Option, + pint: Option, } #[derive(Copy, Clone, Debug, Default, Deserialize)] @@ -98,6 +99,53 @@ pub enum Opendrain { Opendrain, } +#[derive(Copy, Clone, Debug, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +#[repr(u32)] +pub enum PintSlot { + None = 8, + Slot0 = 0, + Slot1 = 1, + Slot2 = 2, + Slot3 = 3, + Slot4 = 4, + Slot5 = 5, + Slot6 = 6, + Slot7 = 7, +} + +impl From for PintSlot { + fn from(slot: u32) -> Self { + match slot { + 0 => PintSlot::Slot0, + 1 => PintSlot::Slot1, + 2 => PintSlot::Slot2, + 3 => PintSlot::Slot3, + 4 => PintSlot::Slot4, + 5 => PintSlot::Slot5, + 6 => PintSlot::Slot6, + 7 => PintSlot::Slot7, + _ => PintSlot::None, + } + } +} + +impl From for PintSlot { + fn from(slot: usize) -> Self { + match slot { + 0 => PintSlot::Slot0, + 1 => PintSlot::Slot1, + 2 => PintSlot::Slot2, + 3 => PintSlot::Slot3, + 4 => PintSlot::Slot4, + 5 => PintSlot::Slot5, + 6 => PintSlot::Slot6, + 7 => PintSlot::Slot7, + _ => PintSlot::None, + } + } +} + #[derive(Copy, Clone, Debug, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Direction { @@ -113,6 +161,25 @@ impl PinConfig { self.alt } + + fn get_pint_slot(&self, used: &mut u8) -> PintSlot { + if let Some(pint_slot) = self.pint { + if self.pin.port > 1 || self.pin.pin > 32 { + panic!("Invalid gpio pin for interrupt"); + } + if pint_slot > 7 { + panic!("Invalid pint setting {} > 7", pint_slot); + } + let mask = 1 << pint_slot; + if (*used & mask) != 0 { + panic!("Duplicate interrupt slot assignment: {:?}", self.pin); + } + *used |= mask; + PintSlot::from(pint_slot) + } else { + PintSlot::None + } + } } impl ToTokens for PinConfig { @@ -140,21 +207,27 @@ impl ToTokens for PinConfig { pub fn codegen(pins: Vec) -> Result<()> { let out_dir = build_util::out_dir(); let dest_path = out_dir.join("pin_config.rs"); - let mut file = std::fs::File::create(dest_path)?; - + let mut file = std::fs::File::create(&dest_path)?; let mut buf = BufWriter::new(Vec::new()); + let mut used_slots = 0u8; + if pins.iter().any(|p| p.name.is_some()) { - writeln!(&mut buf, "use drv_lpc55_gpio_api::Pin;")?; + writeln!(&mut file, "use drv_lpc55_gpio_api::Pin;")?; } writeln!( &mut file, - "fn setup_pins(task : TaskId) -> Result<(), ()> {{" + "fn setup_pins(task : userlib::TaskId) -> Result<(), ()> {{" )?; writeln!(&mut file, "use drv_lpc55_gpio_api::*;")?; writeln!(&mut file, "let iocon = Pins::from(task);")?; for p in pins { writeln!(&mut file, "iocon.iocon_configure(")?; writeln!(&mut file, "{}", p.to_token_stream())?; + writeln!( + &mut file, + "PintSlot::{:?}", + p.get_pint_slot(&mut used_slots) + )?; writeln!(&mut file, ");")?; // Output pins can specify their value, which is set before configuring @@ -183,21 +256,38 @@ pub fn codegen(pins: Vec) -> Result<()> { } match p.name { None => (), - Some(name) => { + Some(ref name) => { let pin = p.pin.get_port_pin(); writeln!(&mut buf, "#[allow(unused)]")?; writeln!( &mut buf, "const {}: Pin = Pin::PIO{}_{};", - name, pin.0, pin.1 + &name, pin.0, pin.1 )?; + + let mut ignore = 0u8; + let slot = p.get_pint_slot(&mut ignore); + if slot != PintSlot::None { + writeln!(&mut buf, "#[allow(unused)]")?; + writeln!( + &mut buf, + "pub const {}_PINT_MASK: u32 = 1 << {};", + &name, slot as u32 + )?; + } } } } writeln!(&mut file, "Ok(())")?; writeln!(&mut file, "}}")?; - write!(file, "{}", String::from_utf8(buf.into_inner()?).unwrap())?; + + write!( + &mut file, + "{}", + String::from_utf8(buf.into_inner()?).unwrap() + )?; + call_rustfmt::rustfmt(&dest_path)?; Ok(()) } diff --git a/chips/lpc55/chip.toml b/chips/lpc55/chip.toml index f3508c2361..2ce48dae0f 100644 --- a/chips/lpc55/chip.toml +++ b/chips/lpc55/chip.toml @@ -77,3 +77,12 @@ size = 0x1000 [bootrom] address = 0x03000000 size = 0x10000 + +[pint] +address = 0x4000_4000 +size = 0x1000 +interrupts = { irq0 = 4, irq1 = 5, irq2 = 6, irq3 = 7, irq4 = 32, irq5 = 33, irq6 = 34, irq7 = 35 } + +[inputmux] +address = 0x4000_6000 +size = 0x1000 diff --git a/drv/lpc55-gpio-api/src/lib.rs b/drv/lpc55-gpio-api/src/lib.rs index 5d6372ae11..3502e2a0f9 100644 --- a/drv/lpc55-gpio-api/src/lib.rs +++ b/drv/lpc55-gpio-api/src/lib.rs @@ -200,6 +200,20 @@ pub enum Value { One = 1, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, AsBytes, FromPrimitive)] +#[repr(u8)] +pub enum PintSlot { + Slot0 = 0, + Slot1 = 1, + Slot2 = 2, + Slot3 = 3, + Slot4 = 4, + Slot5 = 5, + Slot6 = 6, + Slot7 = 7, + None = 8, +} + impl Pins { // Calling into the GPIO task each time can be slow, this function // allows tasks to get the appropriate values to write manually. @@ -233,11 +247,12 @@ impl Pins { invert: Invert, digimode: Digimode, od: Opendrain, + pint_slot: PintSlot, ) { let (_, conf) = Pins::iocon_conf_val(pin, alt, mode, slew, invert, digimode, od); - self.iocon_configure_raw(pin, conf); + self.iocon_configure_raw(pin, conf, pint_slot); } } diff --git a/drv/lpc55-gpio/src/main.rs b/drv/lpc55-gpio/src/main.rs index 3b4b2de569..3e8f475670 100644 --- a/drv/lpc55-gpio/src/main.rs +++ b/drv/lpc55-gpio/src/main.rs @@ -51,6 +51,19 @@ task_slot!(SYSCON, syscon_driver); struct ServerImpl<'a> { gpio: &'a device::gpio::RegisterBlock, + pint: &'a device::pint::RegisterBlock, + inputmux: &'a device::inputmux::RegisterBlock, +} + +impl ServerImpl<'_> { + fn set_pin_direction(&self, port: usize, pin: usize, dir: Direction) { + match dir { + Direction::Input => self.gpio.dirclr[port] + .write(|w| unsafe { w.dirclrp().bits(1 << pin) }), + Direction::Output => self.gpio.dirset[port] + .write(|w| unsafe { w.dirsetp().bits(1 << pin) }), + } + } } impl idl::InOrderPinsImpl for ServerImpl<'_> { @@ -61,13 +74,7 @@ impl idl::InOrderPinsImpl for ServerImpl<'_> { dir: Direction, ) -> Result<(), RequestError> { let (port, pin) = gpio_port_pin_validate(pin); - - match dir { - Direction::Input => self.gpio.dirclr[port] - .write(|w| unsafe { w.dirclrp().bits(1 << pin) }), - Direction::Output => self.gpio.dirset[port] - .write(|w| unsafe { w.dirsetp().bits(1 << pin) }), - } + self.set_pin_direction(port, pin, dir); Ok(()) } @@ -123,6 +130,7 @@ impl idl::InOrderPinsImpl for ServerImpl<'_> { _: &RecvMessage, pin: Pin, conf: u32, + pint_slot: PintSlot, ) -> Result<(), RequestError> { // The LPC55 IOCON Rust API has individual functions for each pin. // This is not easily compatible with our API that involves passing @@ -137,8 +145,145 @@ impl idl::InOrderPinsImpl for ServerImpl<'_> { core::ptr::write_volatile(base as *mut u32, conf); } + // If the GPIO pin is configured for interrupts, then + // interrupt configuration is done before pin configuration. + // INPUTMUX from UM11126: + // Once set up, no clocks are required for the input multiplexer to + // function. The system clock is needed only to write to or read from + // the INPUT MUX registers. Once the input multiplexer is configured, + // disable the clock to the INPUT MUX block in the AHBCLKCTRL register. + // See: Section 4.5.17 “AHB clock control 0”. + match pint_slot { + PintSlot::None => {} + PintSlot::Slot0 + | PintSlot::Slot1 + | PintSlot::Slot2 + | PintSlot::Slot3 + | PintSlot::Slot4 + | PintSlot::Slot5 + | PintSlot::Slot6 + | PintSlot::Slot7 => { + // The INPUTMUX only needs to be turned on during configuration. + let syscon = Syscon::from(SYSCON.get_task_id()); + syscon.enable_clock(Peripheral::Mux); + syscon.leave_reset(Peripheral::Mux); + unsafe { + self.inputmux.pintsel[pint_slot as usize] + .write(|w| w.bits(pin as u32)); + } + syscon.disable_clock(Peripheral::Mux); + + // NOTE: We're only supporting edge-triggered interrupts right now. + // We hard-code ISEL.PMODE as edge-triggered: + // Edge triggered = 0 << PintSlot + // Level triggered = 1 << PintSlot + let mask = 1u32 << (pint_slot as usize); + unsafe { + self.pint.isel.modify(|r, w| w.bits(r.bits() & !mask)); + } + } + } + + Ok(()) + } + + // + // Functions for managing GPIO interrupts: + // + + fn clear_rising( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result<(), idol_runtime::RequestError> { + self.pint.rise.write(|w| unsafe { w.bits(pint_mask) }); + Ok(()) + } + + fn clear_falling( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result<(), idol_runtime::RequestError> { + self.pint.fall.write(|w| unsafe { w.bits(pint_mask) }); + Ok(()) + } + + fn clear_status( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result<(), idol_runtime::RequestError> { + self.pint.ist.write(|w| unsafe { w.bits(pint_mask) }); Ok(()) } + + fn detected_rising( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result> + { + let status = self.pint.rise.read().bits() & pint_mask; + Ok(status) + } + + fn detected_falling( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result> + { + let status = self.pint.fall.read().bits() & pint_mask; + Ok(status) + } + + fn disable_falling( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result<(), idol_runtime::RequestError> { + self.pint.cienf.write(|w| unsafe { w.bits(pint_mask) }); + Ok(()) + } + + fn disable_rising( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result<(), idol_runtime::RequestError> { + self.pint.cienr.write(|w| unsafe { w.bits(pint_mask) }); + Ok(()) + } + + fn enable_falling( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result<(), idol_runtime::RequestError> { + // XXX sanity check pint_mask + self.pint.sienf.write(|w| unsafe { w.bits(pint_mask) }); + Ok(()) + } + + fn enable_rising( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result<(), idol_runtime::RequestError> { + self.pint.sienr.write(|w| unsafe { w.bits(pint_mask) }); + Ok(()) + } + + fn read_status( + &mut self, + _: &RecvMessage, + pint_mask: u32, + ) -> Result> + { + let status = self.pint.ist.read().bits() & pint_mask; + Ok(status) + } } impl NotificationHandler for ServerImpl<'_> { @@ -157,8 +302,14 @@ fn main() -> ! { turn_on_gpio_clocks(); let gpio = unsafe { &*device::GPIO::ptr() }; + let pint = unsafe { &*device::PINT::ptr() }; + let inputmux = unsafe { &*device::INPUTMUX::ptr() }; - let mut server = ServerImpl { gpio }; + let mut server = ServerImpl { + gpio, + pint, + inputmux, + }; let mut incoming = [0; idl::INCOMING_SIZE]; loop { @@ -188,9 +339,13 @@ fn turn_on_gpio_clocks() { syscon.enable_clock(Peripheral::Gpio1); syscon.leave_reset(Peripheral::Gpio1); + + syscon.enable_clock(Peripheral::Pint); + syscon.leave_reset(Peripheral::Pint); } mod idl { + use crate::PintSlot; use drv_lpc55_gpio_api::{Direction, Pin, Value}; include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); diff --git a/drv/lpc55-i2c/src/main.rs b/drv/lpc55-i2c/src/main.rs index c949dab437..bc9a3b8a9b 100644 --- a/drv/lpc55-i2c/src/main.rs +++ b/drv/lpc55-i2c/src/main.rs @@ -159,6 +159,7 @@ fn muck_with_gpios(syscon: &Syscon) { Invert::Disable, Digimode::Digital, Opendrain::Normal, + PintSlot::None, ); iocon.iocon_configure( @@ -169,6 +170,7 @@ fn muck_with_gpios(syscon: &Syscon) { Invert::Disable, Digimode::Digital, Opendrain::Normal, + PintSlot::None, ); } diff --git a/drv/lpc55-sprot-server/src/main.rs b/drv/lpc55-sprot-server/src/main.rs index 1180b0d251..db56a0f88a 100644 --- a/drv/lpc55-sprot-server/src/main.rs +++ b/drv/lpc55-sprot-server/src/main.rs @@ -56,9 +56,7 @@ use drv_sprot_api::{ }; use lpc55_pac as device; use ringbuf::{ringbuf, ringbuf_entry}; -use userlib::{ - sys_irq_control, sys_recv_notification, task_slot, TaskId, UnwrapLite, -}; +use userlib::{sys_irq_control, sys_recv_notification, task_slot, UnwrapLite}; mod handler; diff --git a/drv/user-leds/src/main.rs b/drv/user-leds/src/main.rs index 549fac5ea7..a4357b3451 100644 --- a/drv/user-leds/src/main.rs +++ b/drv/user-leds/src/main.rs @@ -627,6 +627,7 @@ fn enable_led_pins() { Invert::Disable, Digimode::Digital, Opendrain::Normal, + PintSlot::None, ); gpio_driver.iocon_configure( @@ -637,6 +638,7 @@ fn enable_led_pins() { Invert::Disable, Digimode::Digital, Opendrain::Normal, + PintSlot::None, ); // Both LEDs are active low -- so they will light when we set the diff --git a/idl/lpc55-pins.idol b/idl/lpc55-pins.idol index 69e5b66afd..9389598cfc 100644 --- a/idl/lpc55-pins.idol +++ b/idl/lpc55-pins.idol @@ -5,6 +5,7 @@ Interface( args: { "pin": (type: "Pin", recv: FromPrimitive("u32")), "conf": "u32", + "pint_slot": (type: "PintSlot", recv: FromPrimitive("u8")), }, reply: Simple("()"), idempotent: true, @@ -41,5 +42,78 @@ Interface( err: ServerDeath, ) ), + "clear_falling": ( + args: { + "pint_mask": "u32", + }, + reply: Simple("()"), + idempotent: true, + ), + "clear_rising": ( + args: { + "pint_mask": "u32", + }, + reply: Simple("()"), + idempotent: true, + ), + "clear_status": ( + args: { + "pint_mask": "u32", + }, + reply: Simple("()"), + idempotent: true, + ), + "detected_falling": ( + args: { + "pint_mask": "u32", + }, + reply: Result( + ok: "u32", err: ServerDeath, + ) + ), + "detected_rising": ( + args: { + "pint_mask": "u32", + }, + reply: Result( + ok: "u32", err: ServerDeath, + ) + ), + "disable_falling": ( + args: { + "pint_mask": "u32", + }, + reply: Simple("()"), + idempotent: true, + ), + "disable_rising": ( + args: { + "pint_mask": "u32", + }, + reply: Simple("()"), + idempotent: true, + ), + "enable_falling": ( + args: { + "pint_mask": "u32", + }, + reply: Simple("()"), + idempotent: true, + ), + "enable_rising": ( + args: { + "pint_mask": "u32", + }, + reply: Simple("()"), + idempotent: true, + ), + "read_status": ( + args: { + "pint_mask": "u32", + }, + reply: Result( + ok: "u32", err: ServerDeath, + ) + ), } ) diff --git a/task/hiffy/src/lpc55.rs b/task/hiffy/src/lpc55.rs index de050a452b..ea6d4f0e2d 100644 --- a/task/hiffy/src/lpc55.rs +++ b/task/hiffy/src/lpc55.rs @@ -244,7 +244,16 @@ fn gpio_configure( let task = GPIO.get_task_id(); let gpio = drv_lpc55_gpio_api::Pins::from(task); - gpio.iocon_configure(pin, alt, mode, slew, invert, digimode, opendrain); + gpio.iocon_configure( + pin, + alt, + mode, + slew, + invert, + digimode, + opendrain, + PintSlot::None, + ); Ok(0) } From 681cfea56d5453ddc4715503db5cb36fa9eb0719 Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Mon, 18 Nov 2024 18:41:48 -0800 Subject: [PATCH 2/4] changes from PR feedback --- build/lpc55pins/src/lib.rs | 15 +++++---------- drv/lpc55-gpio/src/main.rs | 6 ++---- drv/lpc55-sprot-server/src/main.rs | 4 +++- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/build/lpc55pins/src/lib.rs b/build/lpc55pins/src/lib.rs index c86cef0d5a..3247c1ddd4 100644 --- a/build/lpc55pins/src/lib.rs +++ b/build/lpc55pins/src/lib.rs @@ -208,15 +208,15 @@ pub fn codegen(pins: Vec) -> Result<()> { let out_dir = build_util::out_dir(); let dest_path = out_dir.join("pin_config.rs"); let mut file = std::fs::File::create(&dest_path)?; - let mut buf = BufWriter::new(Vec::new()); - let mut used_slots = 0u8; + let mut used_slots = 0u8; + let mut buf = BufWriter::new(Vec::new()); if pins.iter().any(|p| p.name.is_some()) { - writeln!(&mut file, "use drv_lpc55_gpio_api::Pin;")?; + writeln!(&mut buf, "use drv_lpc55_gpio_api::Pin;")?; } writeln!( &mut file, - "fn setup_pins(task : userlib::TaskId) -> Result<(), ()> {{" + "fn setup_pins(task : TaskId) -> Result<(), ()> {{" )?; writeln!(&mut file, "use drv_lpc55_gpio_api::*;")?; writeln!(&mut file, "let iocon = Pins::from(task);")?; @@ -281,12 +281,7 @@ pub fn codegen(pins: Vec) -> Result<()> { writeln!(&mut file, "Ok(())")?; writeln!(&mut file, "}}")?; - - write!( - &mut file, - "{}", - String::from_utf8(buf.into_inner()?).unwrap() - )?; + write!(file, "{}", String::from_utf8(buf.into_inner()?).unwrap())?; call_rustfmt::rustfmt(&dest_path)?; Ok(()) diff --git a/drv/lpc55-gpio/src/main.rs b/drv/lpc55-gpio/src/main.rs index 3e8f475670..813abd8477 100644 --- a/drv/lpc55-gpio/src/main.rs +++ b/drv/lpc55-gpio/src/main.rs @@ -167,10 +167,8 @@ impl idl::InOrderPinsImpl for ServerImpl<'_> { let syscon = Syscon::from(SYSCON.get_task_id()); syscon.enable_clock(Peripheral::Mux); syscon.leave_reset(Peripheral::Mux); - unsafe { - self.inputmux.pintsel[pint_slot as usize] - .write(|w| w.bits(pin as u32)); - } + self.inputmux.pintsel[pint_slot as usize] + .write(|w| unsafe { w.intpin().bits(pin as u8) }); syscon.disable_clock(Peripheral::Mux); // NOTE: We're only supporting edge-triggered interrupts right now. diff --git a/drv/lpc55-sprot-server/src/main.rs b/drv/lpc55-sprot-server/src/main.rs index db56a0f88a..1180b0d251 100644 --- a/drv/lpc55-sprot-server/src/main.rs +++ b/drv/lpc55-sprot-server/src/main.rs @@ -56,7 +56,9 @@ use drv_sprot_api::{ }; use lpc55_pac as device; use ringbuf::{ringbuf, ringbuf_entry}; -use userlib::{sys_irq_control, sys_recv_notification, task_slot, UnwrapLite}; +use userlib::{ + sys_irq_control, sys_recv_notification, task_slot, TaskId, UnwrapLite, +}; mod handler; From 70ecce90eefa5ee0564f39e7e260bf8ed9d806b3 Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Mon, 25 Nov 2024 19:45:34 -0800 Subject: [PATCH 3/4] Consolidate the various GPIO Pin Interrupt functions into a combined API. The functions: fn {clear,detected,disable,enable}_{rising,falling,status}(PintSlot) and fn read_pint_status(...) become fn pint_op(PintSlot, PintOp, PintCondition) This leaves a couple permutations not covered (enable/disable interrupt at the NVIC level). Those can are left as no-ops for the time being. Future: The unimplemented combinations could return an error/fault if called. --- Cargo.lock | 4 + build/lpc55pins/src/lib.rs | 98 ++++++++-------- drv/lpc55-gpio-api/Cargo.toml | 2 + drv/lpc55-gpio-api/src/lib.rs | 76 ++++++++++++- drv/lpc55-gpio/Cargo.toml | 2 + drv/lpc55-gpio/src/main.rs | 206 +++++++++++++++------------------- drv/lpc55-i2c/src/main.rs | 4 +- drv/user-leds/src/main.rs | 4 +- idl/lpc55-pins.idol | 82 ++------------ task/hiffy/src/lpc55.rs | 9 +- 10 files changed, 233 insertions(+), 254 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88b08c0f2a..6d7affc1b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1378,10 +1378,12 @@ version = "0.1.0" dependencies = [ "drv-lpc55-gpio-api", "drv-lpc55-syscon-api", + "hubpack", "idol", "idol-runtime", "lpc55-pac", "num-traits", + "serde", "userlib", "zerocopy 0.6.6", ] @@ -1394,9 +1396,11 @@ dependencies = [ "cfg-if", "counters", "derive-idol-err", + "hubpack", "idol", "idol-runtime", "num-traits", + "serde", "userlib", "zerocopy 0.6.6", ] diff --git a/build/lpc55pins/src/lib.rs b/build/lpc55pins/src/lib.rs index 3247c1ddd4..83bc3f3b2a 100644 --- a/build/lpc55pins/src/lib.rs +++ b/build/lpc55pins/src/lib.rs @@ -103,7 +103,6 @@ pub enum Opendrain { #[serde(rename_all = "lowercase")] #[repr(u32)] pub enum PintSlot { - None = 8, Slot0 = 0, Slot1 = 1, Slot2 = 2, @@ -114,35 +113,30 @@ pub enum PintSlot { Slot7 = 7, } -impl From for PintSlot { - fn from(slot: u32) -> Self { - match slot { - 0 => PintSlot::Slot0, - 1 => PintSlot::Slot1, - 2 => PintSlot::Slot2, - 3 => PintSlot::Slot3, - 4 => PintSlot::Slot4, - 5 => PintSlot::Slot5, - 6 => PintSlot::Slot6, - 7 => PintSlot::Slot7, - _ => PintSlot::None, +impl TryFrom for PintSlot { + type Error = (); + + fn try_from(v: usize) -> Result { + match v { + 0 => Ok(PintSlot::Slot0), + 1 => Ok(PintSlot::Slot1), + 2 => Ok(PintSlot::Slot2), + 3 => Ok(PintSlot::Slot3), + 4 => Ok(PintSlot::Slot4), + 5 => Ok(PintSlot::Slot5), + 6 => Ok(PintSlot::Slot6), + 7 => Ok(PintSlot::Slot7), + _ => Err(()), } } } -impl From for PintSlot { - fn from(slot: usize) -> Self { - match slot { - 0 => PintSlot::Slot0, - 1 => PintSlot::Slot1, - 2 => PintSlot::Slot2, - 3 => PintSlot::Slot3, - 4 => PintSlot::Slot4, - 5 => PintSlot::Slot5, - 6 => PintSlot::Slot6, - 7 => PintSlot::Slot7, - _ => PintSlot::None, - } +impl PintSlot { + pub fn index(self) -> usize { + self as usize + } + pub fn mask(self) -> u32 { + 1u32 << self.index() } } @@ -162,22 +156,26 @@ impl PinConfig { self.alt } - fn get_pint_slot(&self, used: &mut u8) -> PintSlot { - if let Some(pint_slot) = self.pint { + fn get_pint_slot(&self, used: &mut u32) -> Option { + if let Some(slot_number) = self.pint { if self.pin.port > 1 || self.pin.pin > 32 { panic!("Invalid gpio pin for interrupt"); } - if pint_slot > 7 { - panic!("Invalid pint setting {} > 7", pint_slot); - } - let mask = 1 << pint_slot; - if (*used & mask) != 0 { - panic!("Duplicate interrupt slot assignment: {:?}", self.pin); + if let Ok(pint_slot) = PintSlot::try_from(slot_number) { + let mask = pint_slot.mask(); + if (*used & mask) != 0 { + panic!( + "Duplicate interrupt slot assignment: {:?}", + self.pin + ); + } + *used |= mask; + Some(pint_slot) + } else { + panic!("Invalid pint slot number {}", slot_number); } - *used |= mask; - PintSlot::from(pint_slot) } else { - PintSlot::None + None } } } @@ -209,11 +207,15 @@ pub fn codegen(pins: Vec) -> Result<()> { let dest_path = out_dir.join("pin_config.rs"); let mut file = std::fs::File::create(&dest_path)?; - let mut used_slots = 0u8; + let mut used_slots = 0u32; let mut buf = BufWriter::new(Vec::new()); + if pins.iter().any(|p| p.name.is_some()) { writeln!(&mut buf, "use drv_lpc55_gpio_api::Pin;")?; } + if pins.iter().any(|p| p.pint.is_some()) { + writeln!(&mut buf, "use drv_lpc55_gpio_api::PintSlot;")?; + } writeln!( &mut file, "fn setup_pins(task : TaskId) -> Result<(), ()> {{" @@ -223,11 +225,11 @@ pub fn codegen(pins: Vec) -> Result<()> { for p in pins { writeln!(&mut file, "iocon.iocon_configure(")?; writeln!(&mut file, "{}", p.to_token_stream())?; - writeln!( - &mut file, - "PintSlot::{:?}", - p.get_pint_slot(&mut used_slots) - )?; + if let Some(slot) = p.get_pint_slot(&mut used_slots) { + writeln!(&mut file, "Some(PintSlot::Slot{}),", slot.index())?; + } else { + writeln!(&mut file, "None")?; + } writeln!(&mut file, ");")?; // Output pins can specify their value, which is set before configuring @@ -265,14 +267,14 @@ pub fn codegen(pins: Vec) -> Result<()> { &name, pin.0, pin.1 )?; - let mut ignore = 0u8; - let slot = p.get_pint_slot(&mut ignore); - if slot != PintSlot::None { + let mut ignore = 0u32; + if let Some(slot) = p.get_pint_slot(&mut ignore) { writeln!(&mut buf, "#[allow(unused)]")?; writeln!( &mut buf, - "pub const {}_PINT_MASK: u32 = 1 << {};", - &name, slot as u32 + "pub const {}_PINT_SLOT: PintSlot = PintSlot::Slot{};", + &name, + slot.index(), )?; } } diff --git a/drv/lpc55-gpio-api/Cargo.toml b/drv/lpc55-gpio-api/Cargo.toml index 8f41579c6a..ba459f2012 100644 --- a/drv/lpc55-gpio-api/Cargo.toml +++ b/drv/lpc55-gpio-api/Cargo.toml @@ -9,6 +9,8 @@ cfg-if.workspace = true idol-runtime.workspace = true num-traits.workspace = true zerocopy.workspace = true +serde.workspace = true +hubpack.workspace = true counters = { path = "../../lib/counters" } derive-idol-err = { path = "../../lib/derive-idol-err" } diff --git a/drv/lpc55-gpio-api/src/lib.rs b/drv/lpc55-gpio-api/src/lib.rs index 3502e2a0f9..1b0aa59522 100644 --- a/drv/lpc55-gpio-api/src/lib.rs +++ b/drv/lpc55-gpio-api/src/lib.rs @@ -4,6 +4,8 @@ #![no_std] +use hubpack::SerializedSize; +use serde::{Deserialize, Serialize}; use userlib::{sys_send, FromPrimitive}; use zerocopy::AsBytes; @@ -14,7 +16,7 @@ use zerocopy::AsBytes; // goes 0 - 64 cfg_if::cfg_if! { if #[cfg(any(target_board = "lpcxpresso55s69"))] { - #[derive(Copy, Clone, Debug, FromPrimitive, AsBytes)] + #[derive(Copy, Clone, Debug, FromPrimitive, AsBytes, Deserialize, Serialize, SerializedSize)] #[repr(u32)] pub enum Pin { PIO0_0 = 0, @@ -85,7 +87,7 @@ cfg_if::cfg_if! { } } else { - #[derive(Copy, Clone, Debug, FromPrimitive, AsBytes)] + #[derive(Copy, Clone, Debug, FromPrimitive, AsBytes, Deserialize, Serialize, SerializedSize)] #[repr(u32)] pub enum Pin { PIO0_0 = 0, @@ -200,7 +202,18 @@ pub enum Value { One = 1, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, AsBytes, FromPrimitive)] +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + AsBytes, + FromPrimitive, + Serialize, + Deserialize, + SerializedSize, +)] #[repr(u8)] pub enum PintSlot { Slot0 = 0, @@ -211,7 +224,60 @@ pub enum PintSlot { Slot5 = 5, Slot6 = 6, Slot7 = 7, - None = 8, +} + +impl PintSlot { + pub fn index(self) -> usize { + self as usize + } + pub fn mask(self) -> u32 { + 1u32 << self.index() + } +} + +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + AsBytes, + FromPrimitive, + Serialize, + Deserialize, + SerializedSize, +)] +#[repr(u8)] +pub enum PintOp { + Clear, + Enable, + Disable, + Detected, +} + +#[derive( + Copy, + Clone, + Debug, + Eq, + PartialEq, + AsBytes, + FromPrimitive, + Serialize, + Deserialize, + SerializedSize, +)] +#[repr(u8)] +pub enum PintCondition { + /// Interrupt state for this Pin Interrupt + Status, + /// Rising Edge detection + Rising, + /// Falling Edge detection + Falling, + // TODO: Support Level triggered interrupts. + // High, + // Low, } impl Pins { @@ -247,7 +313,7 @@ impl Pins { invert: Invert, digimode: Digimode, od: Opendrain, - pint_slot: PintSlot, + pint_slot: Option, ) { let (_, conf) = Pins::iocon_conf_val(pin, alt, mode, slew, invert, digimode, od); diff --git a/drv/lpc55-gpio/Cargo.toml b/drv/lpc55-gpio/Cargo.toml index d15394dd74..319e9cca88 100644 --- a/drv/lpc55-gpio/Cargo.toml +++ b/drv/lpc55-gpio/Cargo.toml @@ -8,6 +8,8 @@ idol-runtime = { workspace = true } lpc55-pac = { workspace = true } num-traits = { workspace = true } zerocopy = { workspace = true } +serde = { workspace = true } +hubpack = { workspace = true } drv-lpc55-gpio-api = { path = "../lpc55-gpio-api" } drv-lpc55-syscon-api = { path = "../lpc55-syscon-api" } diff --git a/drv/lpc55-gpio/src/main.rs b/drv/lpc55-gpio/src/main.rs index 813abd8477..7657d0d72f 100644 --- a/drv/lpc55-gpio/src/main.rs +++ b/drv/lpc55-gpio/src/main.rs @@ -130,7 +130,7 @@ impl idl::InOrderPinsImpl for ServerImpl<'_> { _: &RecvMessage, pin: Pin, conf: u32, - pint_slot: PintSlot, + pint_slot: Option, ) -> Result<(), RequestError> { // The LPC55 IOCON Rust API has individual functions for each pin. // This is not easily compatible with our API that involves passing @@ -153,32 +153,23 @@ impl idl::InOrderPinsImpl for ServerImpl<'_> { // the INPUT MUX registers. Once the input multiplexer is configured, // disable the clock to the INPUT MUX block in the AHBCLKCTRL register. // See: Section 4.5.17 “AHB clock control 0”. - match pint_slot { - PintSlot::None => {} - PintSlot::Slot0 - | PintSlot::Slot1 - | PintSlot::Slot2 - | PintSlot::Slot3 - | PintSlot::Slot4 - | PintSlot::Slot5 - | PintSlot::Slot6 - | PintSlot::Slot7 => { - // The INPUTMUX only needs to be turned on during configuration. - let syscon = Syscon::from(SYSCON.get_task_id()); - syscon.enable_clock(Peripheral::Mux); - syscon.leave_reset(Peripheral::Mux); - self.inputmux.pintsel[pint_slot as usize] - .write(|w| unsafe { w.intpin().bits(pin as u8) }); - syscon.disable_clock(Peripheral::Mux); - - // NOTE: We're only supporting edge-triggered interrupts right now. - // We hard-code ISEL.PMODE as edge-triggered: - // Edge triggered = 0 << PintSlot - // Level triggered = 1 << PintSlot - let mask = 1u32 << (pint_slot as usize); - unsafe { - self.pint.isel.modify(|r, w| w.bits(r.bits() & !mask)); - } + if let Some(pint_slot) = pint_slot { + // The INPUTMUX only needs to be turned on during configuration. + let syscon = Syscon::from(SYSCON.get_task_id()); + syscon.enable_clock(Peripheral::Mux); + syscon.leave_reset(Peripheral::Mux); + self.inputmux.pintsel[pint_slot.index()] + .write(|w| unsafe { w.intpin().bits(pin as u8) }); + syscon.disable_clock(Peripheral::Mux); + + // NOTE: We're only supporting edge-triggered interrupts right now. + // We hard-code ISEL.PMODE as edge-triggered: + // Edge triggered = 0 << PintSlot + // Level triggered = 1 << PintSlot + unsafe { + self.pint + .isel + .modify(|r, w| w.bits(r.bits() & !pint_slot.mask())); } } @@ -188,99 +179,76 @@ impl idl::InOrderPinsImpl for ServerImpl<'_> { // // Functions for managing GPIO interrupts: // - - fn clear_rising( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result<(), idol_runtime::RequestError> { - self.pint.rise.write(|w| unsafe { w.bits(pint_mask) }); - Ok(()) - } - - fn clear_falling( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result<(), idol_runtime::RequestError> { - self.pint.fall.write(|w| unsafe { w.bits(pint_mask) }); - Ok(()) - } - - fn clear_status( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result<(), idol_runtime::RequestError> { - self.pint.ist.write(|w| unsafe { w.bits(pint_mask) }); - Ok(()) - } - - fn detected_rising( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result> - { - let status = self.pint.rise.read().bits() & pint_mask; - Ok(status) - } - - fn detected_falling( + fn pint_op( &mut self, _: &RecvMessage, - pint_mask: u32, - ) -> Result> - { - let status = self.pint.fall.read().bits() & pint_mask; - Ok(status) - } - - fn disable_falling( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result<(), idol_runtime::RequestError> { - self.pint.cienf.write(|w| unsafe { w.bits(pint_mask) }); - Ok(()) - } - - fn disable_rising( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result<(), idol_runtime::RequestError> { - self.pint.cienr.write(|w| unsafe { w.bits(pint_mask) }); - Ok(()) - } - - fn enable_falling( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result<(), idol_runtime::RequestError> { - // XXX sanity check pint_mask - self.pint.sienf.write(|w| unsafe { w.bits(pint_mask) }); - Ok(()) - } - - fn enable_rising( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result<(), idol_runtime::RequestError> { - self.pint.sienr.write(|w| unsafe { w.bits(pint_mask) }); - Ok(()) - } - - fn read_status( - &mut self, - _: &RecvMessage, - pint_mask: u32, - ) -> Result> - { - let status = self.pint.ist.read().bits() & pint_mask; - Ok(status) + pint_slot: PintSlot, + op: PintOp, + cond: PintCondition, + ) -> Result, RequestError> { + let mask = pint_slot.mask(); + match op { + PintOp::Clear => { + match cond { + PintCondition::Rising => { + self.pint.rise.write(|w| unsafe { w.bits(mask) }) + } + PintCondition::Falling => { + self.pint.fall.write(|w| unsafe { w.bits(mask) }) + } + PintCondition::Status => { + self.pint.ist.write(|w| unsafe { w.bits(mask) }) + } + } + Ok(None) + } + PintOp::Enable => { + match cond { + // Enable rising edge detection + PintCondition::Rising => { + self.pint.sienr.write(|w| unsafe { w.bits(mask) }) + } + // Enable falling edge detection + PintCondition::Falling => { + self.pint.sienf.write(|w| unsafe { w.bits(mask) }) + } + // XXX This could be enable interrupt + PintCondition::Status => (), + } + Ok(None) + } + PintOp::Disable => { + match cond { + // Disable rising edge detection + PintCondition::Rising => { + self.pint.cienr.write(|w| unsafe { w.bits(mask) }) + } + // Disable falling edge detection + PintCondition::Falling => { + self.pint.cienf.write(|w| unsafe { w.bits(mask) }) + } + // XXX This could be disable interrupt + PintCondition::Status => (), + } + Ok(None) + } + PintOp::Detected => { + Ok(Some( + 0 != match cond { + PintCondition::Rising => { + self.pint.rise.read().bits() & mask + } + PintCondition::Falling => { + self.pint.fall.read().bits() & mask + } + // XXX This could be any interrupt detected + PintCondition::Status => { + self.pint.ist.read().bits() & pint_slot.mask() + } + }, + )) + } + } } } @@ -343,6 +311,8 @@ fn turn_on_gpio_clocks() { } mod idl { + use crate::PintCondition; + use crate::PintOp; use crate::PintSlot; use drv_lpc55_gpio_api::{Direction, Pin, Value}; diff --git a/drv/lpc55-i2c/src/main.rs b/drv/lpc55-i2c/src/main.rs index bc9a3b8a9b..e9ad61eb17 100644 --- a/drv/lpc55-i2c/src/main.rs +++ b/drv/lpc55-i2c/src/main.rs @@ -159,7 +159,7 @@ fn muck_with_gpios(syscon: &Syscon) { Invert::Disable, Digimode::Digital, Opendrain::Normal, - PintSlot::None, + None, ); iocon.iocon_configure( @@ -170,7 +170,7 @@ fn muck_with_gpios(syscon: &Syscon) { Invert::Disable, Digimode::Digital, Opendrain::Normal, - PintSlot::None, + None, ); } diff --git a/drv/user-leds/src/main.rs b/drv/user-leds/src/main.rs index a4357b3451..db85fecd20 100644 --- a/drv/user-leds/src/main.rs +++ b/drv/user-leds/src/main.rs @@ -627,7 +627,7 @@ fn enable_led_pins() { Invert::Disable, Digimode::Digital, Opendrain::Normal, - PintSlot::None, + None, ); gpio_driver.iocon_configure( @@ -638,7 +638,7 @@ fn enable_led_pins() { Invert::Disable, Digimode::Digital, Opendrain::Normal, - PintSlot::None, + None, ); // Both LEDs are active low -- so they will light when we set the diff --git a/idl/lpc55-pins.idol b/idl/lpc55-pins.idol index 9389598cfc..133c2ab38e 100644 --- a/idl/lpc55-pins.idol +++ b/idl/lpc55-pins.idol @@ -3,11 +3,12 @@ Interface( ops: { "iocon_configure_raw": ( args: { - "pin": (type: "Pin", recv: FromPrimitive("u32")), + "pin": "Pin", "conf": "u32", - "pint_slot": (type: "PintSlot", recv: FromPrimitive("u8")), + "pint_slot": "Option", }, reply: Simple("()"), + encoding: Hubpack, idempotent: true, ), "set_dir": ( @@ -42,78 +43,17 @@ Interface( err: ServerDeath, ) ), - "clear_falling": ( - args: { - "pint_mask": "u32", - }, - reply: Simple("()"), - idempotent: true, - ), - "clear_rising": ( - args: { - "pint_mask": "u32", - }, - reply: Simple("()"), - idempotent: true, - ), - "clear_status": ( - args: { - "pint_mask": "u32", - }, - reply: Simple("()"), - idempotent: true, - ), - "detected_falling": ( - args: { - "pint_mask": "u32", - }, - reply: Result( - ok: "u32", err: ServerDeath, - ) - ), - "detected_rising": ( - args: { - "pint_mask": "u32", - }, - reply: Result( - ok: "u32", err: ServerDeath, - ) - ), - "disable_falling": ( - args: { - "pint_mask": "u32", - }, - reply: Simple("()"), - idempotent: true, - ), - "disable_rising": ( - args: { - "pint_mask": "u32", - }, - reply: Simple("()"), - idempotent: true, - ), - "enable_falling": ( + "pint_op": ( args: { - "pint_mask": "u32", - }, - reply: Simple("()"), - idempotent: true, - ), - "enable_rising": ( - args: { - "pint_mask": "u32", - }, - reply: Simple("()"), - idempotent: true, - ), - "read_status": ( - args: { - "pint_mask": "u32", + "pint_slot": "PintSlot", + "op": "PintOp", + "cond": "PintCondition", }, reply: Result( - ok: "u32", err: ServerDeath, - ) + ok: "Option", + err: ServerDeath, + ), + encoding: Hubpack, ), } ) diff --git a/task/hiffy/src/lpc55.rs b/task/hiffy/src/lpc55.rs index ea6d4f0e2d..45d6e02f22 100644 --- a/task/hiffy/src/lpc55.rs +++ b/task/hiffy/src/lpc55.rs @@ -245,14 +245,7 @@ fn gpio_configure( let gpio = drv_lpc55_gpio_api::Pins::from(task); gpio.iocon_configure( - pin, - alt, - mode, - slew, - invert, - digimode, - opendrain, - PintSlot::None, + pin, alt, mode, slew, invert, digimode, opendrain, None, ); Ok(0) From d7bbc5b786ef35b330460c590742fe2d11461c8f Mon Sep 17 00:00:00 2001 From: Ben Stoltz Date: Thu, 14 Nov 2024 10:32:51 -0800 Subject: [PATCH 4/4] Demonstrate GPIO interrupts on the LPC55S69-EVK board. Pressing the USER button generates an interrupt to the button task. A single press will increment the RGB pattern. Quick (~1.5s) successive presses 2nd, 3rd, and subsequent will respecitvely - turn off LEDs - blink LEDs - cycle through RBG pattern including all off. Minor updates to other app.toml files: - add `pint` and `inputmux` peripherals to their `gpio_driver` tasks. - when compiling all targets, some needed slight allocation adjustments not related to this PR. Future: The unimplemented combinations could return an error/fault if called. --- Cargo.lock | 38 +++++ app/lpc55xpresso/app-button.toml | 160 ++++++++++++++++++ idl/button.idol | 43 +++++ task/button-api/Cargo.toml | 28 ++++ task/button-api/build.rs | 10 ++ task/button-api/src/lib.rs | 24 +++ task/button/Cargo.toml | 38 +++++ task/button/build.rs | 33 ++++ task/button/src/main.rs | 276 +++++++++++++++++++++++++++++++ 9 files changed, 650 insertions(+) create mode 100644 app/lpc55xpresso/app-button.toml create mode 100644 idl/button.idol create mode 100644 task/button-api/Cargo.toml create mode 100644 task/button-api/build.rs create mode 100644 task/button-api/src/lib.rs create mode 100644 task/button/Cargo.toml create mode 100644 task/button/build.rs create mode 100644 task/button/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 6d7affc1b4..2a1cc17d6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,21 @@ version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +[[package]] +name = "button-api" +version = "0.1.0" +dependencies = [ + "counters", + "derive-idol-err", + "hubpack", + "idol", + "idol-runtime", + "num-traits", + "serde", + "userlib", + "zerocopy 0.6.6", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -4820,6 +4835,29 @@ dependencies = [ "zerocopy 0.6.6", ] +[[package]] +name = "task-button" +version = "0.1.0" +dependencies = [ + "anyhow", + "arrayvec", + "build-lpc55pins", + "build-util", + "button-api", + "drv-lpc55-gpio-api", + "hubpack", + "idol", + "idol-runtime", + "num-traits", + "quote", + "serde", + "serde_with 3.6.1", + "static-cell", + "unwrap-lite", + "userlib", + "zerocopy 0.6.6", +] + [[package]] name = "task-caboose-reader" version = "0.1.0" diff --git a/app/lpc55xpresso/app-button.toml b/app/lpc55xpresso/app-button.toml new file mode 100644 index 0000000000..03096b44a7 --- /dev/null +++ b/app/lpc55xpresso/app-button.toml @@ -0,0 +1,160 @@ +name = "lpc55xpresso-button" +target = "thumbv8m.main-none-eabihf" +board = "lpcxpresso55s69" +chip = "../../chips/lpc55" +stacksize = 1024 +image-names = ["a", "b"] +fwid = true + +[kernel] +name = "lpc55xpresso" +features = ["dump", "dice-self"] +requires = {flash = 55040, ram = 4096} + +[caboose] +region = "flash" +size = 256 +default = true + +[tasks.jefe] +name = "task-jefe" +priority = 0 +max-sizes = {flash = 16384, ram = 2048} +start = true +features = ["dump"] +stacksize = 1536 +notifications = ["fault", "timer"] +extern-regions = ["sram2"] + +[tasks.jefe.config.allowed-callers] +request_reset = ["update_server"] + +[tasks.hiffy] +name = "task-hiffy" +priority = 5 +features = ["lpc55", "gpio"] +max-sizes = {flash = 32768, ram = 16384 } +stacksize = 2048 +start = true +task-slots = ["gpio_driver", "update_server"] + +[tasks.idle] +name = "task-idle" +priority = 7 +max-sizes = {flash = 256, ram = 256} +stacksize = 256 +start = true + +[tasks.update_server] +name = "lpc55-update-server" +priority = 3 +max-sizes = {flash = 26720, ram = 16704} +stacksize = 8192 +start = true +sections = {bootstate = "usbsram"} +uses = ["flash_controller", "hash_crypt"] +notifications = ["flash-irq", "hashcrypt-irq"] +interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"} +task-slots = [{"syscon" = "syscon_driver"}, "jefe"] + +[tasks.syscon_driver] +name = "drv-lpc55-syscon" +priority = 2 +max-sizes = {flash = 8192, ram = 2048} +uses = ["syscon", "anactrl", "pmc"] +start = true +stacksize = 1000 +task-slots = ["jefe"] + +[tasks.gpio_driver] +name = "drv-lpc55-gpio" +priority = 3 +max-sizes = {flash = 8192, ram = 2048} +uses = ["gpio", "iocon", "pint", "inputmux"] +start = true +stacksize = 1000 +task-slots = ["syscon_driver"] + +[tasks.user_leds] +name = "drv-user-leds" +features = ["lpc55"] +priority = 4 +max-sizes = {flash = 8192, ram = 2048} +start = true +stacksize = 1000 +task-slots = ["gpio_driver"] +notifications = ["timer"] + +[tasks.button] +name = "task-button" +priority = 6 +start = true +notifications = ["timer", "button-irq"] +interrupts = { "pint.irq0" = "button-irq"} +stacksize = 4096 +task-slots = ["gpio_driver", {"syscon" = "syscon_driver"}] + +[tasks.button.config] +pins = [ + { name = "BUTTON", pin = { port = 1, pin = 9}, alt = 0, pint = 0, direction = "input", opendrain = "normal" }, + { name = "RED_LED", pin = { port = 1, pin = 6}, alt = 0, direction = "output", value = true }, + { name = "GREEN_LED", pin = { port = 1, pin = 7}, alt = 0, direction = "output", value = true }, + { name = "BLUE_LED", pin = { port = 1, pin = 4}, alt = 0, direction = "output", value = true }, +] + +[tasks.usart_driver] +name = "drv-lpc55-usart" +priority = 4 +max-sizes = {flash = 8192, ram = 2048} +uses = ["flexcomm0"] +start = true +notifications = ["usart-irq"] +interrupts = {"flexcomm0.irq" = "usart-irq"} +stacksize = 1000 +task-slots = ["gpio_driver", "syscon_driver"] + +[tasks.usart_driver.config] +pins = [ + { pin = { port = 0, pin = 29}, alt = 1}, + { pin = { port = 0, pin = 30}, alt = 1} +] + +[tasks.i2c_driver] +name = "drv-lpc55-i2c" +priority = 4 +uses = ["flexcomm4"] +start = true +stacksize = 1000 +task-slots = ["gpio_driver", "syscon_driver"] + +[tasks.rng_driver] +name = "drv-lpc55-rng" +priority = 3 +max-sizes = {flash = 16384, ram = 4096} +uses = ["rng", "pmc"] +start = true +stacksize = 2200 +task-slots = ["syscon_driver"] + +[tasks.dump_agent] +name = "task-dump-agent" +features = ["no-rot"] +priority = 5 +max-sizes = {flash = 32768, ram = 2240 } +start = true +task-slots = ["jefe"] +stacksize = 1536 +extern-regions = ["sram2"] + +[tasks.attest] +name = "task-attest" +priority = 5 +max-sizes = {flash = 35072, ram = 16384} +stacksize = 12304 +start = false +extern-regions = ["dice_alias", "dice_certs"] + +[signing.certs] +signing-certs = ["../../support/fake_certs/fake_certificate.der.crt"] +root-certs = ["../../support/fake_certs/fake_certificate.der.crt"] +private-key = "../../support/fake_certs/fake_private_key.pem" diff --git a/idl/button.idol b/idl/button.idol new file mode 100644 index 0000000000..b7ea953e5a --- /dev/null +++ b/idl/button.idol @@ -0,0 +1,43 @@ +// User Button and LED API + +Interface( + name: "Button", + ops: { + "press": ( + reply: Result( + ok: "u8", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + "off": ( + reply: Result( + ok: "()", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + "set": ( + args: { + "rgb": "u8", + }, + reply: Result( + ok: "()", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + "blink": ( + description: "blinks the LED at a fixed speed", + args: { + "on": "u32", + "off": "u32", + }, + reply: Result( + ok: "()", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + }, +) diff --git a/task/button-api/Cargo.toml b/task/button-api/Cargo.toml new file mode 100644 index 0000000000..9aac46b542 --- /dev/null +++ b/task/button-api/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "button-api" +version = "0.1.0" +edition = "2021" + +[features] + +[dependencies] +counters = { path = "../../lib/counters" } +derive-idol-err = { path = "../../lib/derive-idol-err" } +hubpack = { workspace = true } +idol-runtime = { workspace = true } +num-traits = { workspace = true } +serde = { workspace = true } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } +zerocopy = { workspace = true } + +[build-dependencies] +idol = { workspace = true } +serde = { workspace = true } + +[lib] +test = false +doctest = false +bench = false + +[lints] +workspace = true diff --git a/task/button-api/build.rs b/task/button-api/build.rs new file mode 100644 index 0000000000..16d6384821 --- /dev/null +++ b/task/button-api/build.rs @@ -0,0 +1,10 @@ +// 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/. + +use std::error::Error; + +fn main() -> Result<(), Box> { + idol::client::build_client_stub("../../idl/button.idol", "client_stub.rs")?; + Ok(()) +} diff --git a/task/button-api/src/lib.rs b/task/button-api/src/lib.rs new file mode 100644 index 0000000000..9a0e2d1f7a --- /dev/null +++ b/task/button-api/src/lib.rs @@ -0,0 +1,24 @@ +// 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/. + +//! API crate for the 'button' task. + +#![no_std] + +use derive_idol_err::IdolError; +use userlib::{sys_send, FromPrimitive}; + +#[derive(Copy, Clone, Debug, FromPrimitive, IdolError, counters::Count)] +pub enum ButtonError { + InvalidValue = 1, + TaskRestarted = 2, +} + +impl From for ButtonError { + fn from(_: idol_runtime::ServerDeath) -> Self { + ButtonError::TaskRestarted + } +} + +include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); diff --git a/task/button/Cargo.toml b/task/button/Cargo.toml new file mode 100644 index 0000000000..e146ca1a2f --- /dev/null +++ b/task/button/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "task-button" +version = "0.1.0" +edition = "2021" + +[dependencies] +drv-lpc55-gpio-api = { path = "../../drv/lpc55-gpio-api"} +button-api = { path = "../button-api"} +arrayvec.workspace = true +hubpack = { workspace = true } +idol-runtime = { workspace = true } +num-traits = { workspace = true } +serde = { workspace = true } +serde_with = { version = "3.3.0", default-features = false, features = ["macros"] } +static-cell = { path = "../../lib/static-cell" } +unwrap-lite = { path = "../../lib/unwrap-lite" } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } +zerocopy = { workspace = true } + +[build-dependencies] +anyhow.workspace = true +idol.workspace = true +serde.workspace = true +quote = { workspace = true } +build-lpc55pins = { path = "../../build/lpc55pins" } +build-util = { path = "../../build/util" } + +[features] +no-ipc-counters = ["idol/no-counters"] + +[[bin]] +name = "task-button" +test = false +doctest = false +bench = false + +[lints] +workspace = true diff --git a/task/button/build.rs b/task/button/build.rs new file mode 100644 index 0000000000..1127157906 --- /dev/null +++ b/task/button/build.rs @@ -0,0 +1,33 @@ +// 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/. + +use anyhow::Result; +use build_lpc55pins::PinConfig; +use idol::{server::ServerStyle, CounterSettings}; +use serde::Deserialize; + +#[derive(Deserialize)] +#[serde(deny_unknown_fields, rename_all = "kebab-case")] +struct TaskConfig { + pins: Vec, +} + +fn main() -> Result<()> { + build_util::expose_target_board(); + build_util::build_notifications()?; + + idol::Generator::new() + .with_counters(CounterSettings::default().with_server_counters(false)) + .build_server_support( + "../../idl/button.idol", + "server_stub.rs", + ServerStyle::InOrder, + ) + .unwrap(); + + let task_config = build_util::task_config::()?; + build_lpc55pins::codegen(task_config.pins)?; + + Ok(()) +} diff --git a/task/button/src/main.rs b/task/button/src/main.rs new file mode 100644 index 0000000000..f344717974 --- /dev/null +++ b/task/button/src/main.rs @@ -0,0 +1,276 @@ +// 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/. + +//! Root of trust for reporting (RoT-R) task. +//! +//! Use the attest-api crate to interact with this task. + +#![no_std] +#![no_main] + +use button_api::ButtonError; +use drv_lpc55_gpio_api::{PintCondition, PintOp, Value}; +// use button_api::*; +use crate::idl::INCOMING_SIZE; +use idol_runtime::{ + // ClientError, Leased, LenLimit, + NotificationHandler, + RequestError, + // R, W, +}; +use userlib::{ + set_timer_relative, sys_irq_control, sys_set_timer, task_slot, TaskId, + UnwrapLite, +}; + +// Time is in approximate ms +const ON_DELAY: u32 = 1 * 1000; +const OFF_DELAY: u32 = ON_DELAY / 2; +const QUICKPRESS: u32 = 1500; + +task_slot!(GPIO, gpio_driver); + +struct ButtonServer { + gpio: drv_lpc55_gpio_api::Pins, + last_button_press: u64, + quick: usize, + + /// LED state + on: bool, + /// On ms or 0 to disable timer + on_ms: u32, + /// Off ms or 0 to disable timer + off_ms: u32, + rgb: u8, +} + +impl ButtonServer { + fn increment(&mut self) -> Result { + self.rgb = (self.rgb + 1) % 8; + let _ = self.update_leds()?; + Ok(self.rgb) + } + + fn to_rgb(v: u8) -> (bool, bool, bool) { + ((v & 0b100) != 0, (v & 0b010) != 0, (v & 0b001) != 0) + } + + fn update_leds(&self) -> Result { + let leds = self.rgb; + let (r, g, b) = if self.on { + Self::to_rgb(leds) + } else { + (false, false, false) + }; + // LED signals are active low. + self.gpio + .set_val(RED_LED, if r { Value::Zero } else { Value::One }); + self.gpio + .set_val(GREEN_LED, if g { Value::Zero } else { Value::One }); + self.gpio + .set_val(BLUE_LED, if b { Value::Zero } else { Value::One }); + Ok(leds) + } + + fn timer_expiry(&mut self) { + if self.on { + // LEDs were on + if self.off_ms > 0 { + self.on = false; + let _ = self.update_leds(); + set_timer_relative(self.off_ms, notifications::TIMER_MASK); + } else { + // no off timer; go to the next pattern and update. + let _ = self.increment(); + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } + } else { + // LEDs were off + if self.on_ms > 0 { + self.on = true; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } else { + // Leave them off and stop timer (this should be a redundant stop). + sys_set_timer(None, notifications::TIMER_MASK); + } + let _ = self.update_leds(); + } + } + + fn handle_button_press(&mut self) -> Result { + let now = userlib::sys_get_timer().now; + let last = self.last_button_press; + self.last_button_press = now; + let delta = now - last; + if delta < QUICKPRESS as u64 { + self.quick += 1; + match self.quick { + // Second press: Stop timer and turn off LEDs + 1 => { + self.on = false; + self.on_ms = 0; + self.off_ms = 0; + sys_set_timer(None, notifications::TIMER_MASK); + } + // Third press: Blink 1s on, 0.5s off. + 2 => { + self.on = true; + self.on_ms = ON_DELAY; + self.off_ms = OFF_DELAY; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } + // Forth and later presses: Increment pattern every second. + _ => { + self.on = true; + self.on_ms = ON_DELAY; + self.off_ms = 0; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } + } + Ok(self.update_leds()?) + } else { + // This is a "first" press outside of the quick press time window. + // Make sure the LEDSs are on and increment the pattern. + self.quick = 0; + self.on = true; + self.on_ms = 0; + self.off_ms = 0; + Ok(self.increment()?) + } + } +} + +impl idl::InOrderButtonImpl for ButtonServer { + /// Simulate a button press + fn press( + &mut self, + _: &userlib::RecvMessage, + ) -> Result> { + self.handle_button_press()?; + Ok(self.rgb) + } + + fn off( + &mut self, + _: &userlib::RecvMessage, + ) -> Result<(), RequestError> { + self.rgb = 0; + let _ = self.update_leds()?; + Ok(()) + } + + fn set( + &mut self, + _: &userlib::RecvMessage, + rgb: u8, + ) -> Result<(), RequestError> { + if rgb >= 8 { + Err(ButtonError::InvalidValue.into()) + } else { + self.rgb = rgb % 8; + let _ = self.update_leds()?; + Ok(()) + } + } + + fn blink( + &mut self, + _: &userlib::RecvMessage, + on: u32, + off: u32, + ) -> Result<(), RequestError> { + self.on_ms = on; + self.off_ms = off; + self.on = true; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + let _ = self.update_leds()?; + Ok(()) + } +} + +impl NotificationHandler for ButtonServer { + fn current_notification_mask(&self) -> u32 { + notifications::TIMER_MASK + notifications::BUTTON_IRQ_MASK + } + + fn handle_notification(&mut self, bits: u32) { + if (bits & notifications::TIMER_MASK) != 0 { + self.timer_expiry() + } + + if (bits & notifications::BUTTON_IRQ_MASK) != 0 { + let detected = self + .gpio + .pint_op( + BUTTON_PINT_SLOT, + PintOp::Detected, + PintCondition::Falling, + ) + .map_or(false, |v| v.unwrap_lite()); + let _ = self.gpio.pint_op( + BUTTON_PINT_SLOT, + PintOp::Clear, + PintCondition::Falling, + ); + let _ = self.gpio.pint_op( + BUTTON_PINT_SLOT, + PintOp::Clear, + PintCondition::Status, + ); + if detected { + let _ = self.handle_button_press(); + } + sys_irq_control(notifications::BUTTON_IRQ_MASK, true); + } + } +} + +#[export_name = "main"] +fn main() -> ! { + let mut buffer = [0u8; INCOMING_SIZE]; + + let gpio_driver = GPIO.get_task_id(); + setup_pins(gpio_driver).unwrap_lite(); + let gpio = drv_lpc55_gpio_api::Pins::from(gpio_driver); + + let mut server = ButtonServer { + gpio, + quick: 0, + last_button_press: 0, + on: true, // LEDs are on + on_ms: 0, + off_ms: 0, // timer inactive, with on_ms > 0, increment + rgb: 0b111, // start with all LEDs on + }; + + // Assume the normal case where the PINT has been reset and no other + // task has fiddled our bits. + // We're not clearing any state from a possible task restart. + let _ = server.gpio.pint_op( + BUTTON_PINT_SLOT, + PintOp::Enable, + PintCondition::Falling, + ); + sys_irq_control(notifications::BUTTON_IRQ_MASK, true); + + let _ = server.update_leds(); + if server.on && server.on_ms > 0 { + set_timer_relative(server.on_ms, notifications::TIMER_MASK); + } else if !server.on && server.off_ms > 0 { + set_timer_relative(server.off_ms, notifications::TIMER_MASK); + } + + loop { + idol_runtime::dispatch(&mut buffer, &mut server); + } +} + +mod idl { + use crate::ButtonError; + + include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); +} + +include!(concat!(env!("OUT_DIR"), "/notifications.rs")); +include!(concat!(env!("OUT_DIR"), "/pin_config.rs"));