Chapter 26: reset.s

September 17, 2026 · View on GitHub

Introduction

After power-on, most peripherals on the RP2350 are held in reset to save power and prevent bus conflicts. Before our firmware can configure GPIO registers, it must explicitly release IO_BANK0 from reset and wait for the release to complete. reset.s contains a single function, Init_Subsystem, that does exactly this.

Complete Source Code

.syntax unified                                  // use unified assembly syntax
.cpu cortex-m33                                  // target Cortex-M33 core
.thumb                                           // use Thumb instruction set

.include "constants.s"

.section .text                                   // code section
.align 2                                         // align to 4-byte boundary

.global Init_Subsystem
.type Init_Subsystem, %function
Init_Subsystem:
.GPIO_Subsystem_Reset:
  ldr   r0, =RESETS_RESET                        // load RESETS->RESET address
  ldr   r1, [r0]                                 // read RESETS->RESET value
  bic   r1, r1, #(1<<6)                          // clear IO_BANK0 bit
  str   r1, [r0]                                 // store value into RESETS->RESET address
.GPIO_Subsystem_Reset_Wait:
  ldr   r0, =RESETS_RESET_DONE                   // load RESETS->RESET_DONE address
  ldr   r1, [r0]                                 // read RESETS->RESET_DONE value
  tst   r1, #(1<<6)                              // test IO_BANK0 reset done
  beq   .GPIO_Subsystem_Reset_Wait               // wait until done
  bx    lr                                       // return

Line-by-Line Walkthrough

Release IO_BANK0 from Reset

  ldr   r0, =RESETS_RESET                        // r0 = 0x40020000
  ldr   r1, [r0]                                 // r1 = current reset register value
  bic   r1, r1, #(1<<6)                          // clear bit 6 (IO_BANK0)
  str   r1, [r0]                                 // write back

The RESETS_RESET register has one bit per peripheral. When a bit is 1, that peripheral is held in reset (inactive). When cleared to 0, the peripheral begins its reset release sequence.

Bit 6 corresponds to IO_BANK0, which controls the GPIO function select and control registers.

The read-modify-write pattern (ldrbicstr) preserves all other bits. Only IO_BANK0 is released; all other peripherals remain in their current state.

Wait for Reset Completion

  ldr   r0, =RESETS_RESET_DONE                   // r0 = 0x40020008
  ldr   r1, [r0]                                 // r1 = current done status
  tst   r1, #(1<<6)                              // test IO_BANK0 done bit
  beq   .GPIO_Subsystem_Reset_Wait               // loop if not done
  bx    lr                                       // done — return

Clearing the reset bit starts the release process, but it is not instantaneous. The peripheral needs clock cycles to initialize its internal state. The RESETS_RESET_DONE register reports completion: bit 6 goes high when IO_BANK0 is fully operational.

The polling pattern is identical to the XOSC stabilization loop:

  +-->  ldr r1, [r0]       read RESET_DONE
  |     tst r1, #(1<<6)    check bit 6
  |     beq --------+
  |                 |
  +-----------------+      (loop while not done)
        |
        v
  bx    lr                 (done — return)

Reset Register Bit Map

The RESETS_RESET register controls many peripherals. Bit 6 is the only one we use:

BitPeripheralOur Usage
0ADCNot used
1BUSCTRLNot used
2DMANot used
3HSTXNot used
4I2C0Not used
5I2C1Not used
6IO_BANK0Released
7IO_QSPINot used
8JTAGNot used
9PADS_BANK0Not used
10PADS_QSPINot used
.........

Why the Wait Is Necessary

Without polling RESET_DONE, the firmware might immediately try to read GPIO registers before IO_BANK0 has completed initialization. The result would be:

  • Reads return zero (registers not yet initialized)
  • Writes are silently ignored
  • GPIO configuration appears to succeed but has no effect

The polling loop guarantees that IO_BANK0 is fully functional before any GPIO register access.

Function Characteristics

Init_Subsystem is a leaf function:

  • No function calls — does not use bl
  • No stack frame — does not push/pop registers
  • Uses only r0, r1 — both are scratch registers (caller-saved)
  • Returns via bx lr — LR is preserved

Local Labels

The function uses two local labels:

LabelPurpose
.GPIO_Subsystem_ResetMarks the reset release code
.GPIO_Subsystem_Reset_WaitPolling loop target

These are file-local (prefixed with .) and serve as documentation anchors.

Summary

  • Init_Subsystem releases IO_BANK0 from reset by clearing bit 6 in RESETS_RESET.
  • It then polls RESETS_RESET_DONE bit 6 until the release is complete.
  • This must happen before any GPIO register access — otherwise reads return zero.
  • The read-modify-write pattern preserves other peripherals' reset state.
  • This is a leaf function using only scratch registers, requiring no stack frame.