diff --git a/app/gemini-bu/app.toml b/app/gemini-bu/app.toml index 0b5db0d52..b19860203 100644 --- a/app/gemini-bu/app.toml +++ b/app/gemini-bu/app.toml @@ -23,12 +23,27 @@ request_reset = ["hiffy"] [tasks.sys] name = "drv-stm32xx-sys" -features = ["h753"] +features = ["h753", "exti"] priority = 1 -max-sizes = {flash = 2048, ram = 2048} -uses = ["rcc", "gpios", "system_flash"] +max-sizes = {flash = 4096, ram = 2048} +uses = ["rcc", "gpios", "system_flash", "syscfg", "exti"] start = true task-slots = ["jefe"] +notifications = ["exti-wildcard-irq"] + +[tasks.sys.interrupts] +"exti.exti0" = "exti-wildcard-irq" +"exti.exti1" = "exti-wildcard-irq" +"exti.exti2" = "exti-wildcard-irq" +"exti.exti3" = "exti-wildcard-irq" +"exti.exti4" = "exti-wildcard-irq" +"exti.exti9_5" = "exti-wildcard-irq" +"exti.exti15_10" = "exti-wildcard-irq" + +[tasks.sys.config.gpio-irqs.rot_irq] +port = "E" +pin = 3 +owner = {name = "sprot", notification = "rot_irq"} [tasks.i2c_driver] name = "drv-stm32xx-i2c-server" @@ -119,7 +134,7 @@ start = true task-slots = ["sys"] uses = ["spi4"] features = ["use-spi-core", "h753", "spi4"] -notifications = ["spi-irq"] +notifications = ["spi-irq", "rot-irq", "timer"] interrupts = {"spi4.irq" = "spi-irq"} [tasks.validate] @@ -303,5 +318,4 @@ clock_divider = "DIV256" [config.sprot] # ROT_IRQ (af=0 for GPIO, af=15 when EXTI is implemneted) -# {port = "D", pins = [0], af = 0}, -rot_irq = { port = "E", pin = 3, af = 0} +rot_irq = { port = "E", pin = 3, af = 15} diff --git a/app/gimlet/base.toml b/app/gimlet/base.toml index df6658690..e9503eb01 100644 --- a/app/gimlet/base.toml +++ b/app/gimlet/base.toml @@ -66,6 +66,11 @@ notifications = ["exti-wildcard-irq"] "exti.exti9_5" = "exti-wildcard-irq" "exti.exti15_10" = "exti-wildcard-irq" +[tasks.sys.config.gpio-irqs.rot_irq] +port = "E" +pin = 3 +owner = {name = "sprot", notification = "rot_irq"} + [tasks.spi2_driver] name = "drv-stm32h7-spi-server" priority = 3 @@ -288,7 +293,7 @@ start = true task-slots = ["sys"] features = ["sink_test", "use-spi-core", "h753", "spi4"] uses = ["spi4"] -notifications = ["spi-irq"] +notifications = ["spi-irq", "rot-irq", "timer"] interrupts = {"spi4.irq" = "spi-irq"} [tasks.validate] diff --git a/app/gimletlet/app.toml b/app/gimletlet/app.toml index 40559d81b..a8bce0abe 100644 --- a/app/gimletlet/app.toml +++ b/app/gimletlet/app.toml @@ -19,6 +19,27 @@ request_reset = ["hiffy", "control_plane_agent", "udprpc"] [tasks.jefe.config.on-state-change] host_sp_comms = "jefe-state-change" +[tasks.sys] +# Enable EXTI in the sys task so that we can notify sprot when the RoT +# raises an IRQ. +features = ["exti"] +notifications = ["exti-wildcard-irq"] +uses = ["syscfg", "exti"] + +[tasks.sys.interrupts] +"exti.exti0" = "exti-wildcard-irq" +"exti.exti1" = "exti-wildcard-irq" +"exti.exti2" = "exti-wildcard-irq" +"exti.exti3" = "exti-wildcard-irq" +"exti.exti4" = "exti-wildcard-irq" +"exti.exti9_5" = "exti-wildcard-irq" +"exti.exti15_10" = "exti-wildcard-irq" + +[tasks.sys.config.gpio-irqs.rot_irq] +port = "D" +pin = 0 +owner = {name = "sprot", notification = "rot_irq"} + [tasks.packrat] name = "task-packrat" priority = 3 @@ -217,14 +238,14 @@ start = true task-slots = ["sys"] features = ["sink_test", "use-spi-core", "h753", "spi3"] uses = ["spi3"] -notifications = ["spi-irq"] +notifications = ["spi-irq", "rot-irq", "timer"] interrupts = {"spi3.irq" = "spi-irq"} [config.sprot] # TODO: This config is inert. Need to implement STM32 build.rs like the LPC55 has. pins = [ - # ROT_IRQ (af=0 for GPIO, af=15 when EXTI is implemented) - { name = "ROT_IRQ", pin = { port = "D", pin = 0, af = 0, direction = "input"}}, + # ROT_IRQ (af=15 for EXTI) + { name = "ROT_IRQ", pin = { port = "D", pin = 0, af = 15, direction = "input"}}, # SPI6 CS repurposed for debugging { name = "DEBUG", pin = { port = "G", pin = 8, af = 0, direction = "output"}} ] diff --git a/app/gimletlet/base-gimletlet2.toml b/app/gimletlet/base-gimletlet2.toml index dffb02ea6..677522298 100644 --- a/app/gimletlet/base-gimletlet2.toml +++ b/app/gimletlet/base-gimletlet2.toml @@ -26,7 +26,7 @@ set_reset_reason = ["sys"] name = "drv-stm32xx-sys" features = ["h753"] priority = 1 -max-sizes = {flash = 2048, ram = 2048} +max-sizes = {flash = 4096, ram = 2048} uses = ["rcc", "gpios", "system_flash"] start = true task-slots = ["jefe"] diff --git a/app/psc/base.toml b/app/psc/base.toml index 050c16fd0..e82c01a85 100644 --- a/app/psc/base.toml +++ b/app/psc/base.toml @@ -34,12 +34,27 @@ request_reset = ["hiffy", "control_plane_agent"] [tasks.sys] name = "drv-stm32xx-sys" -features = ["h753", "no-panic"] +features = ["h753", "exti", "no-panic"] priority = 1 -max-sizes = {flash = 2048, ram = 2048} -uses = ["rcc", "gpios", "system_flash"] +max-sizes = {flash = 4096, ram = 2048} +uses = ["rcc", "gpios", "system_flash", "syscfg", "exti"] start = true task-slots = ["jefe"] +notifications = ["exti-wildcard-irq"] + +[tasks.sys.interrupts] +"exti.exti0" = "exti-wildcard-irq" +"exti.exti1" = "exti-wildcard-irq" +"exti.exti2" = "exti-wildcard-irq" +"exti.exti3" = "exti-wildcard-irq" +"exti.exti4" = "exti-wildcard-irq" +"exti.exti9_5" = "exti-wildcard-irq" +"exti.exti15_10" = "exti-wildcard-irq" + +[tasks.sys.config.gpio-irqs.rot_irq] +port = "D" +pin = 0 +owner = {name = "sprot", notification = "rot_irq"} [tasks.i2c_driver] name = "drv-stm32xx-i2c-server" @@ -155,7 +170,7 @@ start = true task-slots = ["sys"] uses = ["spi4"] features = ["sink_test", "use-spi-core", "h753", "spi4"] -notifications = ["spi-irq"] +notifications = ["spi-irq", "rot-irq", "timer"] interrupts = {"spi4.irq" = "spi-irq"} [tasks.udpecho] diff --git a/app/sidecar/base.toml b/app/sidecar/base.toml index 98cb98c8b..e18eb246d 100644 --- a/app/sidecar/base.toml +++ b/app/sidecar/base.toml @@ -6,7 +6,7 @@ fwid = true [kernel] name = "sidecar" -requires = {flash = 25984, ram = 6256} +requires = {flash = 26176, ram = 6256} features = ["dump"] [caboose] @@ -31,12 +31,27 @@ request_reset = ["hiffy", "control_plane_agent"] [tasks.sys] name = "drv-stm32xx-sys" -features = ["h753", "no-panic"] +features = ["h753", "exti", "no-panic"] priority = 1 -max-sizes = {flash = 2048, ram = 2048} -uses = ["rcc", "gpios", "system_flash"] +max-sizes = {flash = 4096, ram = 2048} +uses = ["rcc", "gpios", "system_flash", "syscfg", "exti"] start = true task-slots = ["jefe"] +notifications = ["exti-wildcard-irq"] + +[tasks.sys.interrupts] +"exti.exti0" = "exti-wildcard-irq" +"exti.exti1" = "exti-wildcard-irq" +"exti.exti2" = "exti-wildcard-irq" +"exti.exti3" = "exti-wildcard-irq" +"exti.exti4" = "exti-wildcard-irq" +"exti.exti9_5" = "exti-wildcard-irq" +"exti.exti15_10" = "exti-wildcard-irq" + +[tasks.sys.config.gpio-irqs.rot_irq] +port = "E" +pin = 3 +owner = {name = "sprot", notification = "rot_irq"} [tasks.update_server] name = "stm32h7-update-server" @@ -113,7 +128,7 @@ start = true task-slots = ["sys"] features = ["sink_test", "use-spi-core", "h753", "spi4"] uses = ["spi4"] -notifications = ["spi-irq"] +notifications = ["spi-irq", "rot-irq", "timer"] interrupts = {"spi4.irq" = "spi-irq"} [tasks.udpecho] diff --git a/drv/stm32h7-sprot-server/src/main.rs b/drv/stm32h7-sprot-server/src/main.rs index 5425be86a..efcc639ea 100644 --- a/drv/stm32h7-sprot-server/src/main.rs +++ b/drv/stm32h7-sprot-server/src/main.rs @@ -16,6 +16,7 @@ use drv_stm32xx_sys_api as sys_api; use hubpack::SerializedSize; use idol_runtime::{NotificationHandler, RequestError}; use ringbuf::*; +use sys_api::IrqControl; use userlib::*; cfg_if::cfg_if! { @@ -363,31 +364,102 @@ impl Io { // Poll ROT_IRQ until asserted (true) or deasserted (false). // - // We sleep and poll for what should be long enough for the RoT to queue - // a response. - // - // TODO: Use STM32 EXTI as an interrupt allows for better performance and - // power efficiency. - // - // STM32 EXTI allows for 16 interrupts for GPIOs. - // Each of those can represent Pin X from a GPIO bank (A through K) - // So, only one bank's Pin 3, for example, can have the #3 interrupt. - // For ROT_IRQ, we would configure for the falling edge to trigger - // the interrupt. That configuration should be specified in the app.toml - // for the board. Work needs to be done to generalize the EXTI facility. - // But, hacking in one interrupt as an example should be ok to start things - // off. + // We do this by asking the `sys` task to notify us when the GPIO pin's + // state changes (using EXTI), and waiting for either that or timeout + // determined based on `max_sleep`. fn wait_rot_irq(&mut self, desired: bool, max_sleep: u32) -> bool { - let mut slept = 0; + use notifications::{sprot::TIMER_MASK, ROT_IRQ_MASK}; + // Determine our edge sensitivity for the interrupt. If we want to wait + // for the line to be asserted, we want to wait for a rising edge. If + // the line is currently asserted, and we're waiting for it to be + // *deasserted*, we want to wait for a falling edge. + let sensitivity = match desired { + true => sys_api::Edge::Rising, + false => sys_api::Edge::Falling, + }; + self.sys.gpio_irq_configure(ROT_IRQ_MASK, sensitivity); + + // Enable the interrupt. + self.sys + .gpio_irq_control(ROT_IRQ_MASK, IrqControl::Enable) + // Just unwrap this, because the `sys` task should never panic. + .unwrap_lite(); + + // Determine the deadline after which we'll give up, and start the clock. + let deadline = sys_get_timer() + .now + // Values passed to `max_sleep` are all constants that are small + // enough that this will almost certainly not overflow unless the SP + // has been running without a reset for at least a couple million + // years. Using saturating arithmetic here lets us avoid a bounds + // check. + .saturating_add(max_sleep as u64); + sys_set_timer(Some(deadline), TIMER_MASK); + + let mut irq_fired = false; while self.is_rot_irq_asserted() != desired { - if slept == max_sleep { + // Wait to be notified either by the timeout or by the ROT_IRQ pin + // changing state. + const MASK: u32 = TIMER_MASK | ROT_IRQ_MASK; + let notif = sys_recv_notification(MASK); + + // First, check if the IRQ has fired. We do this by checking if the + // ROT_IRQ notification bit has been posted, and then asking the + // `sys` task to confirm that we actually got the IRQ. + // + // N.B. that we check this *before* checking for the timer + // notification bit, because it's possible *both* notifications were + // posted before we were scheduled again, and if the IRQ did fire, + // we'd prefer to honor that. + irq_fired = notif & ROT_IRQ_MASK != 0 + && self + .sys + // If the IRQ hasn't fired, leave it enabled, otherwise, + // if it has fired, don't re-enable the IRQ. + .gpio_irq_control(ROT_IRQ_MASK, IrqControl::Check) + // Sys task shouldn't panic. + .unwrap_lite(); + if irq_fired { + break; + } + + // If the timer notification was posted, and the GPIO IRQ + // notification wasn't, we've waited for the timeout. Too bad! + if notif & TIMER_MASK != 0 { + // Disable the IRQ, so that we don't get the notification later + // while in `recv`. + self.sys + .gpio_irq_control( + notifications::ROT_IRQ_MASK, + IrqControl::Disable, + ) + .unwrap_lite(); + + // Record the timeout. self.stats.timeouts = self.stats.timeouts.wrapping_add(1); ringbuf_entry!(Trace::RotReadyTimeout); return false; } - hl::sleep_for(1); - slept += 1; } + + // Ensure the timer gets unset before returning, to avoid frightening + // our IPC server when it's waiting in recv without expecting a timer to + // go off. + sys_set_timer(None, TIMER_MASK); + // If the IRQ didn't fire, let's also disable it, so that it also + // doesn't go off later. + if !irq_fired { + self.sys + .gpio_irq_control( + notifications::ROT_IRQ_MASK, + IrqControl::Disable, + ) + .unwrap_lite(); + } + + // We return `true` here regardless of `irq_fired`, because we may not + // have looped at all, if the line was asserted before we started + // waiting for the IRQ. The timeout case returns early, above. true } } @@ -1097,7 +1169,8 @@ impl idl::InOrderSpRotImpl for ServerImpl { impl NotificationHandler for ServerImpl { fn current_notification_mask(&self) -> u32 { - // We don't use notifications, don't listen for any. + // Neither our timer nor our GPIO IRQ notifications are needed while the + // server is in recv. 0 }