Files
Embedded-Hacking/RA-0x0001-GPIO-Output
2025-10-25 17:25:25 -04:00
..
2025-10-25 16:49:33 -04:00
2025-10-25 17:25:25 -04:00
2025-10-05 12:00:31 -07:00
2025-10-25 17:25:25 -04:00
2025-10-25 17:25:25 -04:00
2025-10-05 08:30:01 -07:00
2025-10-05 08:30:01 -07:00

FREE Reverse Engineering Self-Study Course HERE


RP2350 Blink Driver

An RP2350 blink driver written entirely in Assembler.

Code

/**
 * FILE: main.s
 *
 * DESCRIPTION:
 * RP2350 Bare-Metal GPIO16 Blink, Coprocessor Version.
 * 
 * BRIEF:
 * Minimal baremetal LED blink on the RP2350 using the direct coprocessor
 * (MCRR) instructions to manipulate GPIO control registers. This bypasses
 * SDK abstractions and demonstrates registerlevel control in assembler.
 * Clocks the external crystal oscillator (XOSC) at 14.5MHz.
 *
 * AUTHOR: Kevin Thomas
 * CREATION DATE: October 24, 2025
 * UPDATE DATE: October 25, 2025
 */

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

/**
 * Memory addresses and constants.
 */
.equ STACK_TOP,                   0x20082000               // top of non-secure SRAM
.equ STACK_LIMIT,                 0x2007A000               // stack limit (32 KB below top)
.equ XOSC_BASE,                   0x40048000               // base address of XOSC
.equ XOSC_CTRL,                   XOSC_BASE + 0x00         // XOSC->CTRL
.equ XOSC_STATUS,                 XOSC_BASE + 0x04         // XOSC->STATUS
.equ XOSC_STARTUP,                XOSC_BASE + 0x0C         // XOSC->STARTUP
.equ PPB_BASE,                    0xE0000000               // base address of PPB
.equ CPACR,                       PPB_BASE + 0x0ED88       // PPB_BASE->CPACR
.equ CLOCKS_BASE,                 0x40010000               // base address of CLOCKS
.equ CLK_PERI_CTRL,               CLOCKS_BASE + 0x48       // CLOCKS->CLK_PERI_CTRL
.equ RESETS_BASE,                 0x40020000               // base address of RESETS
.equ RESETS_RESET,                RESETS_BASE + 0x0        // RESETS->RESET
.equ RESETS_RESET_CLEAR,          RESETS_BASE + 0x3000     // RESETS->RESET_CLEAR
.equ RESETS_RESET_DONE,           RESETS_BASE + 0x8        // RESETS->RESET_DONE
.equ IO_BANK0_BASE,               0x40028000               // base address of IO_BANK0
.equ IO_BANK0_GPIO16_CTRL_OFFSET, 0x84                     // IO_BANK0->GPIO16 offset
.equ PADS_BANK0_BASE,             0x40038000               // base address of PADS_BANK0
.equ PADS_BANK0_GPIO16_OFFSET,    0x44                     // PADS_BANK0->GPIO16 offset

/**
 * Initialize the .vectors section. The .vectors section contains vector
 * table and Reset_Handler.
 */
.section .vectors, "ax"                                    // vector table section
.align 2                                                   // align to 4-byte boundary

/**
 * Vector table section.
 */
.global _vectors                                           // export _vectors symbol
_vectors:
  .word STACK_TOP                                          // initial stack pointer
  .word Reset_Handler + 1                                  // reset handler (Thumb bit set)

/**
 * @brief   Reset handler for RP2350.
 *
 * @details Entry point after reset. Performs:
 *          - Stack initialization
 *          - Coprocessor enable
 *          - GPIO16 pad/function configuration
 *          - Branches to main() which contains the blink loop
 *
 * @param   None
 * @retval  None
 */
.global Reset_Handler                                      // export Reset_Handler symbol
.type Reset_Handler, %function                        
Reset_Handler:
  BL    Init_Stack                                         // initialize MSP/PSP and limits
  BL    Init_XOSC                                          // initialize external crystal oscillator
  BL    Enable_XOSC_Peri_Clock                             // enable XOSC peripheral clock
  BL    Init_Subsystem                                     // initialize subsystems
  BL    Enable_Coprocessor                                 // enable CP0 coprocessor
  B     main                                               // branch to main loop
.size Reset_Handler, . - Reset_Handler

/**
 * @brief   Initialize stack pointers.
 *
 * @details Sets Main and Process Stack Pointers (MSP/PSP) and their limits.
 *
 * @param   None
 * @retval  None
 */
.type Init_Stack, %function
Init_Stack:
  LDR   R0, =STACK_TOP                                     // load stack top
  MSR   PSP, R0                                            // set PSP
  LDR   R0, =STACK_LIMIT                                   // load stack limit
  MSR   MSPLIM, R0                                         // set MSP limit
  MSR   PSPLIM, R0                                         // set PSP limit
  LDR   R0, =STACK_TOP                                     // reload stack top
  MSR   MSP, R0                                            // set MSP
  BX    LR                                                 // return

/**
 * @brief   Init XOSC and wait until it is ready.
 *
 * @details Configures and initializes the external crystal oscillator (XOSC).
 *          Waits for the XOSC to become stable before returning.
 *
 * @param   None
 * @retval  None
 */
.type Init_XOSC, %function
Init_XOSC:
  LDR   R0, =XOSC_STARTUP                                  // load XOSC_STARTUP address
  LDR   R1, =0x00C4                                        // set delay 50,000 cycles
  STR   R1, [R0]                                           // store value into XOSC_STARTUP
  LDR   R0, =XOSC_CTRL                                     // load XOSC_CTRL address
  LDR   R1, =0x00FABAA0                                    // set 1_15MHz, freq range, actual 14.5MHz
  STR   R1, [R0]                                           // store value into XOSC_CTRL
.Init_XOSC_Wait:
  LDR   R0, =XOSC_STATUS                                   // load XOSC_STATUS address
  LDR   R1, [R0]                                           // read XOSC_STATUS value
  TST   R1, #(1<<31)                                       // test STABLE bit
  BEQ   .Init_XOSC_Wait                                    // wait until stable bit is set
  BX    LR                                                 // return

/**
 * @brief   Enable XOSC peripheral clock.
 *
 * @details Sets the peripheral clock to use XOSC as its AUXSRC.
 *
 * @param   None
 * @retval  None
 */
.type Enable_XOSC_Peri_Clock, %function
Enable_XOSC_Peri_Clock:
  LDR   R0, =CLK_PERI_CTRL                                 // load CLK_PERI_CTRL address
  LDR   R1, [R0]                                           // read CLK_PERI_CTRL value
  ORR   R1, R1, #(1<<11)                                   // set ENABLE bit
  ORR   R1, R1, #(4<<5)                                    // set AUXSRC: XOSC_CLKSRC bit
  STR   R1, [R0]                                           // store value into CLK_PERI_CTRL
  BX    LR                                                 // return

/**
 * @brief   Init subsystem.
 *
 * @details Initiates the various subsystems by clearing their reset bits.
 *
 * @param   None
 * @retval  None
 */
.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

/**
 * @brief   Enable coprocessor access.
 *
 * @details Grants full access to coprocessor 0 via CPACR.
 *
 * @param   None
 * @retval  None
 */
.type Enable_Coprocessor , %function
Enable_Coprocessor:
  LDR   R0, =CPACR                                         // load CPACR address
  LDR   R1, [R0]                                           // read CPACR value
  ORR   R1, R1, #(1<<1)                                    // set CP0: Controls access priv coproc 0 bit
  ORR   R1, R1, #(1<<0)                                    // set CP0: Controls access priv coproc 0 bit
  STR   R1, [R0]                                           // store value into CPACR
  DSB                                                      // data sync barrier
  ISB                                                      // instruction sync barrier
  BX    LR                                                 // return

/**
 * Initialize the .text section. 
 * The .text section contains executable code.
 */
.section .text                                             // code section
.align 2                                                   // align to 4-byte boundary

/**
 * @brief   Main application entry point.
 *
 * @details Implements the infinite blink loop.
 *
 * @param   None
 * @retval  None
 */
.global main                                               // export main
.type main, %function                                      // mark as function
main:
.Push_Registers:
  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack
.GPIO16_Config:
  LDR   R0, =PADS_BANK0_GPIO16_OFFSET                      // load PADS_BANK0_GPIO16_OFFSET
  LDR   R1, =IO_BANK0_GPIO16_CTRL_OFFSET                   // load IO_BANK0_GPIO16_CTRL_OFFSET
  LDR   R2, =16                                            // load GPIO number
  BL    GPIO_Config                                        // call GPIO_Config
.Loop:
  LDR   R0, =16                                            // load GPIO number
  BL    GPIO_Set                                           // call GPIO_Set
  LDR   R0, =500                                           // 500ms
  BL    Delay_MS                                           // call Delay_MS
  LDR   R0, =16                                            // load GPIO number
  BL    GPIO_Clear                                         // call GPIO_Clear
  LDR   R0, =500                                           // 500ms
  BL    Delay_MS                                           // call Delay_MS
  B     .Loop                                              // loop forever
.Pop_Registers:
  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack
  BX    LR                                                 // return to caller

/**
 * @brief   Configure GPIO.
 *
 * @details Configures a GPIO pin's pad control and function select.
 *
 * @param   R0 - PAD_OFFSET
 * @param   R1 - CTRL_OFFSET
 * @param   R2 - GPIO
 * @retval  None
 */
.type GPIO_Config, %function
GPIO_Config:
.GPIO_Config_Push_Registers:
  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack
.GPIO_Config_Modify_Pad:
  LDR   R4, =PADS_BANK0_BASE                               // load PADS_BANK0_BASE address
  ADD   R4, R4, R0                                         // PADS_BANK0_BASE + PAD_OFFSET
  LDR   R5, [R4]                                           // read PAD_OFFSET value
  BIC   R5, R5, #(1<<7)                                    // clear OD bit
  ORR   R5, R5, #(1<<6)                                    // set IE bit
  BIC   R5, R5, #(1<<8)                                    // clear ISO bit
  STR   R5, [R4]                                           // store value into PAD_OFFSET
.GPIO_Config_Modify_CTRL:
  LDR   R4, =IO_BANK0_BASE                                 // load IO_BANK0 base
  ADD   R4, R4, R1                                         // IO_BANK0_BASE + CTRL_OFFSET
  LDR   R5, [R4]                                           // read CTRL_OFFSET value
  BIC   R5, R5, #0x1F                                      // clear FUNCSEL
  ORR   R5, R5, #0x05                                      // set FUNCSEL 0x05->SIO_0
  STR   R5, [R4]                                           // store value into CTRL_OFFSET
.GPIO_Config_Enable_OE:
  LDR   R4, =1                                             // enable output
  MCRR  P0, #4, R2, R4, C4                                 // gpioc_bit_oe_put(GPIO, 1)
.GPIO_Config_Pop_Registers:
  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack
  BX    LR                                                 // return

/**
 * @brief   GPIO set.
 *
 * @details Drives GPIO output high via coprocessor.
 *
 * @param   R0 - GPIO
 * @retval  None
 */
.type GPIO_Set, %function
GPIO_Set:
.GPIO_Set_Push_Registers:
  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack
.GPIO_Set_Execute:
  LDR   R4, =1                                             // enable output
  MCRR  P0, #4, R0, R4, C0                                 // gpioc_bit_out_put(GPIO, 1)
.GPIO_Set_Pop_Registers:
  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack
  BX    LR                                                 // return

/**
 * @brief   GPIO clear.
 *
 * @details Drives GPIO output high via coprocessor.
 *
 * @param   R0 - GPIO
 * @retval  None
 */
.type GPIO_Clear, %function
GPIO_Clear:
.GPIO_Clear_Push_Registers:
  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack
.GPIO_Clear_Execute:
  LDR   R4, =0                                             // disable output
  MCRR  P0, #4, R0, R4, C0                                 // gpioc_bit_out_put(GPIO, 1)
.GPIO_Clear_Pop_Registers:
  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack
  BX    LR                                                 // return

/**
 * @brief   Delay_MS.
 *
 * @details Delays for R0 milliseconds. Conversion: loop_count = ms * 3600
 *          based on a 14.5MHz clock.
 *
 * @param   R0 - milliseconds
 * @retval  None
 */
.type Delay_MS, %function
Delay_MS:
.Delay_MS_Push_Registers:
  PUSH  {R4-R12, LR}                                       // push registers R4-R12, LR to the stack
.Delay_MS_Check:
  CMP   R0, #0                                             // if MS is not valid, return
  BLE   .Delay_MS_Done                                     // branch if less or equal to 0 
.Delay_MS_Setup:
  LDR   R4, =3600                                          // loops per MS based on 14.5MHz clock
  MUL   R5, R0, R4                                         // MS * 3600
.Delay_MS_Loop:
  SUBS  R5, R5, #1                                         // decrement counter
  BNE   .Delay_MS_Loop                                     // branch until zero
.Delay_MS_Done:
  POP   {R4-R12, LR}                                       // pop registers R4-R12, LR from the stack
  BX    LR                                                 // return

/**
 * Test data and constants.
 * The .rodata section is used for constants and static data.
 */
.section .rodata                                           // read-only data section

/**
 * Initialized global data.
 * The .data section is used for initialized global or static variables.
 */
.section .data                                             // data section

/**
 * Uninitialized global data.
 * The .bss section is used for uninitialized global or static variables.
 */
.section .bss                                              // BSS section

License

Apache License 2.0