Skip to content

Feature request: Early GPIO startup control & state latching #959

@Gadgetoid

Description

@Gadgetoid

We have a few applications of the RP2040 chip and Pico W-as-a-SOM where it's important for us to:

  1. Assert some GPIO pin really early on startup
  2. Know the state the GPIO was in as early as possible on startup

When writing a basic program in C++ it's possible to assert a pin and get the startup GPIO state some 15ms after a cold boot, simply by doing it in the top of main, but this depends on there being absolutely no preinit/init happening before main is reached.

In real-world scenarios there could be large quantities of zero init BSS (MicroPython for Pico W's 166k gc_heap) or initialized SRAM which - in our tests - can move any existing methods of hooking into early startup to some 150ms after the rising edge of RESET. This is enough to miss even a user button press, much less a transient pulse from a hardware peripheral.

We can do better by modifying crt0.S to init the RP2040 peripherals before SRAM load and zeroing the BSS. This is achieved by splitting runtime_init into runtime_init and runtime_reset_peripherals the latter of which is called before BSS and SRAM init, immediately followed by runtime_user_init (this is a very rough proof of concept) which latches our VSYS_EN.

This takes a (MicroPython PicoW) 150ms time from rising edge of RESET to VSYS_EN_PIN HIGH right down to about 8ms, beating even a vanilla C++ example with code in main.

Why? In our application VSYS_EN must be held high for the Pico W to be powered. We're shutting the chip fully down and powering it back on with a mix of external events. We'd like to know what event "woke" the chip, even if it's transient. Down to a limit, of course. And bearing in mind the chip is off so interrupts et al are no go.

To summarize:

  • We'd like to read GPIO state and assert pin(s) really early
  • Is this "feature" something worthy of cleaning up for Pico SDK?
  • Is this really the right way to do it?

I imagine this being configured with something like -DPICO_STARTUP_PIN_STATE and -DPICO_STARTUP_PIN_MASK so a build can be configured with any permutation of (not system essential) pins set to a desired state. This may include pulls but should probably only focus on SIO.

For the programming minded (or those who want to deploy this themselves) here's a diff showing my changes to runtime.c and crt0.S:

diff --git a/src/rp2_common/pico_runtime/runtime.c b/src/rp2_common/pico_runtime/runtime.c
index 70dd3bb..3326992 100644
--- a/src/rp2_common/pico_runtime/runtime.c
+++ b/src/rp2_common/pico_runtime/runtime.c
@@ -17,6 +17,7 @@
 #include "hardware/clocks.h"
 #include "hardware/irq.h"
 #include "hardware/resets.h"
+#include "hardware/gpio.h"
 
 #include "pico/mutex.h"
 #include "pico/time.h"
@@ -61,11 +62,19 @@ void runtime_install_stack_guard(void *stack_bottom) {
                    | 0x10000000; // XN = disable instruction fetch; no other bits means no permissions
 }
 
-void runtime_init(void) {
+void runtime_user_init(void) {
+    const uint VSYS_EN_PIN = 10;
+    gpio_init(VSYS_EN_PIN);
+    gpio_set_dir(VSYS_EN_PIN, GPIO_IN);
+    gpio_pull_up(VSYS_EN_PIN);
+}
+
+void runtime_reset_peripherals(void) {
     // Reset all peripherals to put system into a known state,
     // - except for QSPI pads and the XIP IO bank, as this is fatal if running from flash
     // - and the PLLs, as this is fatal if clock muxing has not been reset on this boot
     // - and USB, syscfg, as this disturbs USB-to-SWD on core 1
+
     reset_block(~(
             RESETS_RESET_IO_QSPI_BITS |
             RESETS_RESET_PADS_QSPI_BITS |
@@ -86,7 +95,9 @@ void runtime_init(void) {
             RESETS_RESET_UART1_BITS |
             RESETS_RESET_USBCTRL_BITS
     ));
+}
 
+void runtime_init(void) {
     // pre-init runs really early since we need it even for memcpy and divide!
     // (basically anything in aeabi that uses bootrom)
 
diff --git a/src/rp2_common/pico_standard_link/crt0.S b/src/rp2_common/pico_standard_link/crt0.S
index b2992f6..80367da 100644
--- a/src/rp2_common/pico_standard_link/crt0.S
+++ b/src/rp2_common/pico_standard_link/crt0.S
@@ -9,6 +9,7 @@
 #include "hardware/regs/addressmap.h"
 #include "hardware/regs/sio.h"
 #include "pico/binary_info/defs.h"
+#include "hardware/regs/resets.h"
 
 #ifdef NDEBUG
 #ifndef COLLAPSE_IRQS
@@ -225,6 +226,17 @@ _reset_handler:
     cmp r0, #0
     bne hold_non_core0_in_bootrom
 
+    ldr r1, =runtime_reset_peripherals
+    blx r1
+
+    ldr r1, =runtime_user_init
+    blx r1
+
+    // Read GPIO state for front buttons and store
+    movs r3, 0xd0                // Load 0xd0 into r3
+    lsls r3, r3, 24              // Shift left 24 to get 0xd0000000
+    ldr r6, [r3, 4]              // Load GPIO state (0xd0000004) into r6
+
     // In a NO_FLASH binary, don't perform .data copy, since it's loaded
     // in-place by the SRAM load. Still need to clear .bss
 #if !PICO_NO_FLASH
@@ -251,6 +263,10 @@ bss_fill_test:
     cmp r1, r2
     bne bss_fill_loop
 
+    // runtime_wakeup_gpio_state gets zero init above
+    ldr r2, =runtime_wakeup_gpio_state   // Load output var addr into r2
+    str r6, [r2]                        // Store r6 to r2
+
 platform_entry: // symbol for stack traces
     // Use 32-bit jumps, in case these symbols are moved out of branch range
     // (e.g. if main is in SRAM and crt0 in flash)
@@ -314,6 +330,19 @@ data_cpy_table:
 runtime_init:
     bx lr
 
+.weak runtime_user_init
+.type runtime_user_init,%function
+.thumb_func
+runtime_user_init:
+    bx lr
+
+.weak runtime_reset_peripherals
+.type runtime_reset_peripherals,%function
+.thumb_func
+runtime_reset_peripherals:
+    bx lr
+
+
 // ----------------------------------------------------------------------------
 // If core 1 somehow gets into crt0 due to a spectacular VTOR mishap, we need to
 // catch it and send back to the sleep-and-launch code in the bootrom. Shouldn't
@@ -345,3 +374,9 @@ __get_current_exception:
 .align 2
     .equ HeapSize, PICO_HEAP_SIZE
 .space HeapSize
+
+.section .data._reset_handler
+.global runtime_wakeup_gpio_state
+.align 4
+runtime_wakeup_gpio_state:
+.word 0x00000000
\ No newline at end of file

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions

    pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy